オブジェクト指向: ポリモーフィズム

概要

多態性・多相性
オーバーライド(=基底クラスと同名の、しかし異なる機能のメソッドを定義する事)を用いてコーディング量を減らすテクニック。
インスタンスからはどのクラスか?を意識せず動的に多様な動作を実現できる。

メソッドテーブル

ポリモーフィズムの代表的テクニックの1つ。

//基底クラス
public class TextReader
{
  //共通メソッドを定義
  public virtual void open() { }
  public virtual void close() { }

  public virtual string read() { return ""; }
}

//派生クラス1
public class FileReader : TextReader
{
  protected string pathName;
  public FileReader(String pathName)
  {
    this.pathName = pathName;
  }

  //基底クラスの共通メソッドを継承
  public override void open() { }
  public override void close() { }

  public override string read() { return ""; }
}

//派生クラス2
public class NetworkReader : TextReader
{
  protected string id;
  protected string pass;
  public NetworkReader(String id, String pass)
  {
    this.id = id;
    this.pass = pass;
  }

  //基底クラスの共通メソッドを継承
  public override void open() { }
  public override void close() { }

  public override string read() { return ""; }
}

public partial class Form1 : Form
{
  private void Form1_Load(object sender, EventArgs e)
  {
    //ローカル上のファイルを読み込むクラス
    FileReader file = new FileReader(pathName: @"D:\Test");
    //ネットワーク上のファイルを読み込むクラス
    NetworkReader net = new NetworkReader(id: "yone", pass: "aaa");
    
    異なるクラスであっても同じ基底クラスを継承していれば引数として渡せる
    int charCountAtLocal = getCount(reader:file);
    int charCountAtNet = getCount(reader:net);

    異なる機能を持つメソッドを共通の記述で処理できている。
    ⇒ポリモーフィズム
  }
  
  private int getCount(TextReader reader)
  {
    ※引数:readerは基底クラスTextReader型
    TextReaderクラスだけでなく、FileReaderクラス、NetworkReaderクラスのインスタンスでも受け取り可能
    
    int charCount = 0;
    while (true)
    {
      //異なるクラス・異なる挙動であっても、
       メソッドがoverrideされているので共通の記述でメソッドを呼び出せる

      string myStr = reader.read();
      ※read()はFileReaderクラスのread()、NetworkReaderクラスのread()、どちらもOK
      
      //終了条件等、省略
      charCount++;
    }
    return charCount;
  }
}

暗黙の変換/キャスト

object_cast1
クラスにおいては、
基底クラスの型 ← 派生クラスのインスタンス :OK
派生クラスの型 ← 基底クラスのインスタンス :NG
cast2
機能は、基底クラス < 派生クラス だが、
メモリ使用容量は、派生クラス < 基底クラス
プログラム言語 変数/暗黙の型変換」参照
プログラム言語 変数/キャスト」参照

なおインターフェイスとその実装クラスにおけるキャストも上記と同じ
object_cast2

アップキャスト

基底クラスの変数に派生クラスのインスタンスを格納する事。
(暗黙の型変換)
class Super {}
class Sub : Super {}
Super super = new Sub();

class Base {
  public static final String FOO = "Base";
}
class Sub extends Base {
  public static final String FOO = "Sub";
}
public static void main(String[] args) {
  Base b = new Base();
  Sub s = new Sub();
  
  System.out.print(Base.FOO); ⇒Base
  System.out.print(Sub.FOO); ⇒Sub
  ※FOOはstaticなのでインスタンス化無しで参照可能
  
  System.out.print(b.FOO); ⇒Base
  System.out.print(s.FOO); ⇒Sub
  
  System.out.print(((Base)s).FOO); ⇒Base
  System.out.print(((Sub)b).FOO); ⇒コンパイルエラー
  
}

ダウンキャスト

派生クラスの変数に基底クラスのインスタンスを格納する事
(キャスト)
class Super {}
class Sub : Super {}
Sub sub = (Sub) new Super();

ポリモーフィズムの実装確認

public interface I {
  void Method();
}

public class A implements I
{
  public void Method() {
    System.out.println("Method");
  }
  public void MethodA(){
    System.out.println("MethodA");
  }
}

public class B extends A
{
  public void MethodB(){
    System.out.println("MethodB");
  }
}

public static void main(String[] args) {
  I i = new B();
  A a = new B();
  B b = new B();
  
  i.Method();
  i.MethodA(); // ×
  i.MethodB(); // ×
  
  a.Method();
  a.MethodA();
  a.MethodB(); // ×
  
  b.Method();
  b.MethodA();
  b.MethodB();
  
  静的な型(クラス)で定義されているメソッドしか呼び出せない
}

動的クラス操作

型判定

public class A
{
  public virtual void method()
  {
    Console.WriteLine(“A”);
  }
}

public class B : A
{
  public override void method()
  {
    Console.WriteLine(“B\n”);
  }
}

static void Main(string[] args)
{

  静的な型/動的な型
  A a1 = new A();   //静的:A / 動的:A
  A a2 = new B();   //静的:A / 動的:B
  B b = new B();   //静的:B / 動的:B
  
  
  変数と動的な型が同じ時にtrue
  if (a1 is B) { /* 通らない */ }
  if (a2 is B) { /* 通らない */ }
  if (b1 is A) { /* 通らない */ }
  if (a1 is A) { /* 通る */ }
  if (a2 is A) { /* 通る */ }
  if (b1 is B) { /* 通る */ }
}

型情報、クラス情報

ポリモーフィズム型インスタンスの実行時の挙動
//動的な型の仮想メソッドが呼ばれる
a1.method();  // ⇒ A
a2.method();  // ⇒ B
b1.method();  // ⇒ B

静的なクラス情報取得
Type 変数 = typeof(クラス名);
⇒ 変数 : クラス情報

System.Type typeA = typeof(A);
System.Type typeB = typeof(B);

動的なクラス情報取得
Type 変数= インスタンス.GetType();
⇒ 変数 : クラス情報

System.Type DinamicTypeA1 = a1.GetType();
System.Type DinamicTypeA2 = a2.GetType();
System.Type DinamicTypeB = b.GetType();

プロパティ情報を取得
PropertyInfo myProperty = a1.GetType().GetProperty(name: "prop");
PropertyInfo[] myProperties = a1.GetType().GetProperties();

メソッド情報を取得(C#)
MethodInfo myMethod = a1.GetType().GetMethod(name: "method");
MethodInfo[] myMethods = a1.GetType().GetMethods();

動的インスタンス化

文字列→インスタンス
型 変数名 = (型) Class.forName("名前空間\クラス名").newInstance();

インスタンス→インスタンス
class 型 {
 public 型 メソッド名(){
  return (型) clone();
 }
}