オブジェクト指向: 継承

概要

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

継承方法

通常
<?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 Parent
{
protected:
  int _member;
public:
  int Member();
  void Member(int member);
};

int Parent::Member()
{
  return this->_member;
}
void Parent::Member(int member)
{
  this->_member = member;
}

継承修飾子
public:基底クラスで設定したアクセス修飾子の設定をそのまま引き継ぐ
protected:基底クラスでpublicだったものを、protectedにして引き継ぐ。他はそのまま。
private:基底クラスのメンバを全てprivateで引き継ぐ。

class Child : 継承修飾子(例:public) Parent
{
public:
  void Plus();
};

void Child::Plus()
{
  this->_member *= 2;
}

int main(array<System::String ^> ^args)
{
  Child *child = new Child();
  child->Member(10);
  child->Plus();
  // →20
  Console::WriteLine(child->Member());
  
  return 0;
}

※基底クラス
Public Class Parent
 Public Sub SubRoutine1()
  ~
 End Sub
End Class

※派生クラス
Public Class Child Inherits Parent
 Public Sub SubRoutine2()
  ~
 End Sub
End Class

Public Static Sub main()
  Dim c As Child()
  Call c.SubRoutine1() ’OK
  Call c.SubRoutine2() ’OK
End Sub

※スーパークラス(基底クラス)
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
}

多重継承
class Child :
private Parent1,
protected Parent2,
public Parent3
{
private:
  ~
public:
  ~
};

同一の基底クラスを継承した派生クラスを多重継承した場合に、
基底クラスのメンバーを派生クラスにおいて重複して継承してしまう問題を回避する場合の処理については、
オブジェクト指向: 特殊なクラス/仮想基本クラス」参照

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

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);
 }
}

Parentクラスの定義は「継承方法」参照

class Child : public Parent
{
public:
  void Plus();
};

void Child::Plus()
{
  this->_member *= 2;
}

class GrandChild : public Child
{
public:
  再定義するメソッドを定義
  virtual void Plus();
};

void Child::Plus()
{
  // 基底クラスのメソッド呼び出し
  Child::Plus();
  (_member *= 2)
  this->_member += 5;
}

int main(array<System::String ^> ^args)
{
  Child *child = new Child();
  child->Member(10);
  child->Plus();
  Console::WriteLine(child->Member());
  // →20
  delete child;
  
  
  GrandChild *gChild = new GrandChild();
  gChild ->Member(10);
  gChild ->Plus();
  Console::WriteLine(gChild ->Member());
  // →25
  delete gChild ;
  
  return 0;
}

Public Class Parent
 Private _member As Integer

 ※Overridesされるメソッド
 Public Overridable Sub SubRoutine()
  member += 10
 End Sub

End Class

Public Class Child
 Inherits Parent

 Public Overrides Sub SubRoutine()
  Call MsgBox(member)
 End Sub

End Class

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

※スーパークラス(基底クラス)
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#では全てのオーバーライドされるメソッドは強制的に仮想関数

class MyParent
{
public:
 virtual void fnFunc();
};

void MyParent::fnFunc()
{
 cout << "MyParent" << '\n';
 return;
}

class MyChild : public MyParent
{
public:
 virtual void fnFunc();
};

void MyChild::fnFunc()
{
 cout << "MyChild" << '\n';
 return;
}

int _tmain(int argc, _TCHAR* argv[])
{

 MyParent *parent;
 
 parent = new MyParent();
 parent->fnFunc();
 →MyParent(virtual:無)
 →MyParent(virtual:有)

 parent = new MyChild();
 parent->fnFunc();
 →MyParent(virtual:無)
 →MyChild(virtual:有)

 Virtual無し時:静的な型のメソッドが呼ばれる。
 Virtual有り時:動的な型のメソッドが呼ばれる。

 return 0;
}

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

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

インターフェース

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

<?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);
}

C++ではインターフェイス用の予約語は無い。
ゲッター/セッターの事をインターフェイスと呼ぶ(人もいる)
派生クラスに基底クラスのメンバーメソッドの再定義を強制する場合、
インターフェイス用クラスにて純粋仮想関数(抽象クラス)をプロトタイプ定義する。

抽象クラス」参照

Interface faceTest
 Property member As Integer
 Sub SubRoutin1()
End Interface

Public Class clsParent
 Implements faceTest

 Private _member As Integer
 Public Property member As Integer Implements faceTest.member
  ~
 End Property

 Public Sub SubRoutin1() Implements faceTest.SubRoutin1
  ~
 End Sub

 Public Sub SubRoutin2()
  ~
 End Sub
End Class

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;;
}

抽象クラス

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

抽象クラス(純粋仮想関数を含むクラス)
class MyParent
{
public:
 純粋仮想関数(=抽象メソッド)
 virtual void fnFunc() = 0;
};

void MyParent::fnFunc()
{
 ~
 return;
}

class MyChild : public MyParent{};

int _tmain(int argc, _TCHAR* argv[])
{

 MyParent *parent;
 
 parent = new MyParent();
 コンパイルエラー(抽象クラスをインスタンス化できない)
 
 parent = new MyChild();
 コンパイルエラー(派生クラスは仮想関数を実装していない)
 
 return 0;
}

class MyChild : public MyParent{
public:
 仮想関数を実装
 void fnFunc();
};

void MyChild::fnFunc()
{
 基底クラスの仮想関数を呼ぶ
 MyParent::fnFunc();
 return;
}

int _tmain(int argc, _TCHAR* argv[])
{

 MyParent *parent = new MyChild();
 parent->fnFunc();
 派生クラスのfnFunc()の中から、基底クラスのfnFunc()が呼ばれる。
 
 return 0;
}

Public MustInherit Class Person
  Public MustOverride Function GetName() As String
  
  ※エラー。抽象メソッドは中身を書けない。
  Public MustOverride Function GetName() As String
    Return ""
  End Function
End Class

‘抽象クラスを継承(1)
⇒エラー(抽象メソッドを再定義していない)
Public Class Taro
  Inherits Person
End Class

‘抽象クラスを継承(2)
Public Class Taro
  Inherits Person
  Public Overrides Function GetName() As String
    Return "My Name Is 太郎"
  End Function
End Class

Public Class Form1
  Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

    ‘エラー。抽象クラスはインスタンス化できない。
    ’Dim person As Person = New Person

    Dim taro As Person = New Taro ‘⇒ OK
    Dim someone As Person = New Taro ‘⇒ OK
  End Sub
End Class

抽象クラス/抽象メソッド
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);

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

抽象クラスを継承した場合、抽象メソッドのアクセス修飾子のスコープを狭めるオーバーライドはできない。
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{ ~ }