オブジェクト指向: 継承

概要

クラスを参照し、参照先クラスのメンバー(変数、メソッド)全てを利用する事。
更に機能を付け足す事ができる。

継承方法

通常
<?php
namespace src\name_parent;

継承禁止
final class classTest{
  ~
}

class classP{
  private $val;

  public function getVal(){
    return $this->val;
  }

  public function setVal($prm){
    $this->val = $prm;
  }
}

<?php
namespace src\name_child;
use src\name_parent\classP;
require_once 'src/name_parent/classP.php';

class classC extends classP{}

<?php
require_once 'src/name_child/classC.php';
use src\name_child\classC;

$c = new classC();
$c->setVal("aaa");
echo $c->getVal();

class Parent:
 〜

class Child(Parent):
 〜

※基底クラス
private class Parent
{
 public void SubRoutine1(){ ~; }
}

※派生クラス
private class Child : Parent
{
 public void SubRoutine2(){ ~; }
}

public static void main()
{
 Child c = new Child();
 c.SubRoueint1(); //OK
 c.SubRoueint2(); //OK
}

※スーパークラス(基底クラス)
class SuperClass {
  public void SubRoueint1(){}
}

※サブクラス(派生クラス)
public class SubClass extends SuperClass {
  public void SubRoueint2(){}
}

public static void main()
{
 Child s = new SubClass();
 s.SubRoueint1(); //OK
 s.SubRoueint2(); //OK
}

継承時のメンバーへのアクセス優先順位

public class A
{
  public String myStr = "A";
  public void myMethod() {
    System.out.println(this.myStr);
  }
}

public class B extends A
{
  public String myStr = "B";
}

public static void main(String[] args) {
  A a = new A();
  A b = new B();
  
  System.out.println(a.myStr); ⇒ A
  System.out.println(b.myStr); ⇒ A
  ※型のインスタンス変数が優先される
  
  a.myMethod(); ⇒ A
  b.myMethod(); ⇒ A
  ※定義されているメソッドの場所が優先される
  
  B b = new B();
  System.out.println(b.myStr); ⇒ B
  ※型のインスタンス変数が優先される
  b.myMethod(); ⇒ A
  ※定義されているメソッドの場所が優先される
}

Objectクラス

全クラスの基底クラス
全クラスはObjectクラスのメソッドを使用できる

Objectクラスの主なメソッド

メソッド名 機能
boolean equals(Object obj) オブジェクトの比較結果取得
final Class<?> getClass() 実行時クラス情報取得
int hashCode() ハッシュコードの取得
String toString() 文字列情報取得
void finalize() 廃棄処理オブジェクトへの参照が無い場合にガベージコレクタによって呼び出される

オーバーロード

マジックメソッド

未定義のプロパティ、関数呼び出し時に呼び出される関数
コンストラクタ、デストラクタもマジックメソッドの一種

class magic_method
{
  private $values = array();

  public function __set($name, $value){
    print "未定義SET呼び出しエラー\r\n";
    $this->values[$name] = $value;
  }

  public function __get($name){
    print "未定義GET呼び出しエラー\r\n";
    return $this->values[$name];
  }

  public function __isset($name){
    print "未定義ISSET呼び出しエラー\r\n";
    return isset($this->values[$name]);
  }

  public function __unset($name){
    print "未定義UNSET呼び出しエラー\r\n";
    unset($this->values[$name]);
  }
  
  public function __call($name){
    print "未定義CALL呼び出しエラー\r\n";
  }
}

require_once 'magic_method.php';

$magic = new magic_method();

$magic->magic = "PHP";
→ 未定義SET呼び出しエラー
print $magic->magic;
→ 未定義GET呼び出しエラー
→ PHP
print isset($magic->magic);
→ 未定義ISSET呼び出しエラー
→ 1(true)
unset($magic->magic);
→ 未定義UNSET呼び出しエラー
print $magic->magic("PHP");
→ 未定義CALL呼び出しエラー

オーバーライド

ポリモーフィズムを実現する為に、基底クラスと同名の、しかし異なる機能のメソッドを定義する事。

private class Parent
{
 public int member{ get; set; }
 
 public virtual void SubRoutine()
 { this.member += 10; }
}

private class Child : Parent
{
 //オーバーライド
 public override void SubRoutine()
 {
  Console.WriteLine(member);
 }
}

オーバーライド条件
・メソッド名、引数が同じ
・戻り値が同じ、またはそのサブクラス
・修飾子が同じ、またはスーパークラスより広い
※この条件から外れるとエラーか、オーバーロード扱いされる

※スーパークラス(基底クラス)
class SuperClass {
  //オーバーライドされるメソッドに修飾子はない(virtual等)
  public void method1(){}
  public void method2(){}
  private void method3(){}
}

※サブクラス(派生クラス)
public class SubClass extends SuperClass {
  //オーバーライドするメソッドに修飾子はない(override等)
  サブクラスのインスタンスから呼ばれたメソッドはサブ⇒スーパーの順序で探されて実行される。
  public void method1(){}
  
  エラー
  publicのメソッドをprivateでオーバーライドできない。
  private void method2(){}
  
  元メソッドより広い範囲のアクセス修飾子はOK
  public void method3(){}
}

<?php
namespace src\name_parent;

class classP{
  private $val;
  
  オーバーライド禁止
  public final function test(){
    ~
  }
  
  public function getVal(){
    return $this->val;
  }
  
  public function setVal($prm){
    $this->val = $prm;
  }
}

<?php
namespace src\name_child;
use src\name_parent\classP;

require_once 'src/name_parent/classP.php';

class classC extends classP{
  public function getVal(){
    return parent::getVal() . "(overrided)";
  }
}

<?php
require_once 'src/name_child/classC.php';
use src\name_child\classC;

$c = new classC();
$c->setVal("aaa");
echo $c->getVal();
→ aaa(overrided)

隠蔽

派生クラスにおいて、規定クラスと同名のメンバーを新しく定義する事
その際、基底クラスのメンバーへはbase修飾しない限りアクセスできなくなる(隠蔽される)。

class Parent
{
 public int i;
 public void Show()
 {
  Console.WriteLine(value: this.i);
 }
}

class Child : Parent
{
 新しく定義(規定クラスのiを隠蔽)
 new int i;
 
 public Child(int prm)
 {
  i = prm;
  this.i = prm;
 }
 
 public int GetChildI()
 {
  return i;
  return this.i;
 }
 
 public int GetParentI()
 {
  return base.i;
 }
 
 コンパイルエラー:基底クラスと同名のメソッドは定義できない
 public void Show()
 {
  Console.WriteLine(value: i);
  Console.WriteLine(value: this.i);
 }

 
 新しく定義(規定クラスのShow()を隠蔽)
 new public void Show()
 {
  Console.WriteLine(value: i);
  Console.WriteLine(value: this.i);
 }
}

class Program
{
 static void Main(string[] args)
 {

  Child c = new Child(10);
  c.i = 5;
  int ret1 = c.i;
  ret1:5
  int ret2 = c.GetChildI();
  ret2:10
  int ret3 = c.GetParentI();
  ret3:5
  
  c.Show();
  →10
 }
}

ポリモーフィズム

多態性・多相性
異なる機能を持つメソッドを、共通の記述で呼び出すこと。
オブジェクト指向: ポリモーフィズム」参照

仮想関数
C#では全てのオーバーライドされるメソッドは強制的に仮想関数
JAVAでは全てのオーバーライドされるメソッドは強制的に仮想関数

抽象クラス

抽象クラス
中身のないメンバー(抽象メソッド)を持つクラス。
抽象クラスはインスタンス化できない。
抽象メソッド
抽象クラス内のメソッド。
派生クラスがオーバーライドしなければならない。
インターフェイスとの違い
継承先にメンバーの定義を強制できる機能は同じ。
インターフェイス:複数継承できる。
抽象クラス:1つしか継承できない。

//抽象クラス
public abstract class Person
{
  //抽象メソッド
  public abstract string getName(); ⇒ OK
  
  ※エラー。抽象メソッドは中身を書けない。
  public abstract string getName(){ return ""; }
}

//抽象クラスを継承(1)
⇒エラー(抽象メソッドを再定義していない)
public class Taro : Person {}

//抽象クラスを継承(2) ⇒OK
public class Taro : Person
{
  //抽象メソッドをオーバーライド
  public override string getName()
  {
    return "My Name Is 太郎";
  }
}

public partial class Form1 : Form
{
  private void Form1_Load(object sender, EventArgs e)
  {
    // ↓ はエラー。抽象クラスはインスタンス化できない。
    Person person = new Person();
    
    Taro taro = new Taro(); ⇒ OK
    Person someone = new Taro(); ⇒ OK
  }
}

抽象クラス/抽象メソッド
abstract修飾子を付ける
インスタンス化禁止
抽象クラス内の抽象メソッドは必ずサブクラスで再定義しなければならない

abstract class Super{
  abstract void methodA{}
  void methodB(){ ~ }
}

抽象クラスを継承した抽象クラスでは、抽象メソッドを再定義する必要はない。
再定義は具象クラスで必要。
abstract class Sub extends Super{
  abstract void methodC{}
}

class SubSub extends Sub {
  void methodA(){ ~ }
  void methodC(){ ~ }
}

目的
・スーパークラスでサブクラスに対し、メソッド名を強制できる
 (同機能のメソッドの乱立を防ぐ、コーディング規約的使い方)
・ポリモーフィズムの利用の為

インタフェイスを実装した抽象クラス

interface MyInterFace
{
  int myInt = 0; // 固定値。static final が自動的に付加される
  void myMethod(); // 抽象メソッド。abstract が自動的に付加される
}

public abstract class Parent implements MyInterFace {
  // 抽象クラスではインターフェイスにおける抽象メソッドを再定義しなくても良い
  // 抽象クラスの具象クラスでは必要
}

public class Child extends Parent {
  @Override
  public void myMethod() {}
}

Child c = new Child();
c.myMethod();

<?php
namespace src\name_ab;

抽象クラス宣言
abstract class classAb
{
  const CONST_VAL = "aaa";

  抽象メソッド宣言(中身無し)
  abstract public function method($pam);
}

<?php
namespace src\name_parent;
require_once 'src/name_ab/classAb.php';
use src\name_ab\classAb;

class classP extends classAb
{
  public function method($pam){
    print classAb::CONST_VAL;
  }
}

<?php
namespace src\name_child;
use src\name_parent\classP;
require_once 'src/name_parent/classP.php';

class classC extends classP{}
<?php
require_once 'src/name_child/classC.php';
use src\name_child\classC;

$c = new classC();
print $c->method(123);

インターフェース

実装先にメンバーの定義を強制できる機能
メソッドは実装先で再定義する事を前提としている為中身を記述しない事もできる。
※ { ~ } の省略
インタフェイスをインスタンス化する事はできない。
クラスの継承と違い、メンバーを明示的に定義しなければならない。

<?php
namespace src\name_I;

interface I{
  const CONST_VAL = "III";
  public function iMethod();
}

<?php
namespace src\name_parent;
require_once 'src/name_I/I.php';
use src\name_I\I;

class classP implements I
{
  public function iMethod(){
    print I::CONST_VAL;
  }
}

<?php
namespace src\name_child;
use src\name_parent\classP;
require_once 'src/name_parent/classP.php';

class classC extends classP{
}

<?php
require_once 'src/name_child/classC.php';
use src\name_child\classC;

$c = new classC();
print $c->iMethod();

interface Person {
 name: string;
 age: number;
}

let person: Person = {
 name: “type_script”;
 age: 10;
};

console.log(person); → { name: “type_script”, age: 10 }
console.log(person.age); → 10

public interface faceTest
{
 インターフェイスではフィールドを定義できない(≠JAVA)
 int member; ×
 void SubRoutin1();
}

public class clsParent : faceTest
{
 //インターフェイス・メンバーの実装
 インターフェイス・メンバーはpublicで宣言する必要あり
 public void SubRoutin1(){ ~ }

//インターフェイスには無いメンバーの定義
 public int member1{ get; set; }
 public int member2{ get; }
 public void SubRoutin2(int prm1, int prm2)
 { ~ }
}

public void main(int prm1, int prm2)
{
 clsParent p = new clsParent();
 p.member1 = 5;
 p.SubRoutin1();
 p.SubRoutin2(5,5);
}

interface
・定義できるメソッドは抽象メソッドのみ
※コンパイル時にpablic abstractが付与される
 (インターフェイスではprivate/protected修飾子禁止)
・実装時、オーバーライドする際にはpublic修飾必須。
・インスタンス変数を定数として定義できる。
※コンパイル時にpublic static finalが付与される
 (インターフェイスでは初期値が必要)。
・インスタンス化不可
・インターフェイスを継承するインターフェイスはextendsで継承
・インターフェイスにおいて、インターフェイスの複数継承は可。
・インターフェイスを実装するクラスはimplementsで実装
・インターフェイスは複数実装可能。
・継承と実装を同時に行う場合、継承、実装の順に定義

interface MyInter1{
  abstract void method1();
}

interface MyInter2{
  abstract void method2();
}

class MyClass1 implements MyInter1, MyInter2{
  public void method1(){}
  public void method2(){}
}

interface MyInter3 extends MyInter1, MyInter2 {
  public static final String constValue = "ConstValue";
  void method1();
  void method2();
}

class MyClass2 extends MyClass1 implements MyInter1, MyInter2, MyInter3{
  public void method1(){};
  public void method2(){};
  
  String a = MyInter3.constValue;;
}

抽象クラス継承時のスコープ

抽象クラスを継承した場合、抽象メソッドのアクセス修飾子のスコープを狭めるオーバーライドはできない。
private < アクセス修飾子無し < protected < public ※JAVA abstract class Super{   private abstract int priMethod(); ⇒NG
  ※コンパイルエラー(抽象クラスにおいてprivateアクセス修飾子は禁止)
  abstract int method(); ⇒OK
  protected abstract int proMethod(); ⇒OK
  public abstract int pubMethod(); ⇒OK
}
class Sub extends Super{
  int method(){ return 0; } ⇒OK
  protected int method(){ return 0; } ⇒OK
  public int method(){ return 0; } ⇒OK
  
  int proMethod(){ return 0; } ⇒NG
  protected int proMethod(){ return 0; } ⇒OK
  public int proMethod(){ return 0; } ⇒OK
  int pubMethod(){ return 0; } ⇒NG
  protected int pubMethod(){ return 0; } ⇒NG
  public int pubMethod(){ return 0; } ⇒OK
}

オーバーライド/継承禁止

public abstract class Parent
{
  public virtual void method() { }
}

public class Child1 : Parent
{
  //オーバーライド禁止(自分は良い。以降の派生クラスで禁止)
  public sealed override void method(){}
}

public class Grandson : Child1
{
  public override void method() { }
  ⇒ ビルドエラー(オーバーライド失敗)
}

//継承禁止(自分は良い。以降の派生クラスで禁止)
public sealed class Child2 : Parent
{
  public override void method(){}
}

public class Grandson : Child2
{
  ⇒ ビルドエラー(継承失敗)
}

final修飾子
変数、メソッド、クラスに付加可。

//定数
final int a = 5;

//オーバーライド禁止(オーバーロードは可)
final void method(){ ~ }

//継承禁止
final class MyClass{ ~ }

final修飾子
変数、メソッド、クラスに付加可。

//定数
final int a = 5;

//オーバーライド/オーバーロード禁止
final void method(){ ~ }

//継承禁止
final class MyClass{ ~ }