1概要

すべてのオブジェクト指向プログラミング(OOP)言語は、抽象化、カプセル化、継承、およびポリモーフィズムという4つの基本特性を示すことが必要です。

この記事では、2つのコア型のポリモーフィズム、つまり

静的またはコンパイル時ポリモーフィズム



動的またはランタイム

ポリモーフィズム** __について説明します。

静的多相はコンパイル時に強制され、動的多相は実行時に実現されます。


2.


静的多型


https://en.wikipedia.org/wiki/Template


metaprogramming#Static

polymorphism[Wikipedia]によると、静的多型はコンパイル時に解決されるため、実行時の仮想テーブル検索

が不要になる

多型の模倣です。

たとえば、ファイルマネージャアプリケーションの

TextFile

クラスには、

read()

メソッドと同じシグネチャを持つ3つのメソッドがあります。

public class TextFile extends GenericFile {
   //...

    public String read() {
        return this.getContent()
          .toString();
    }

    public String read(int limit) {
        return this.getContent()
          .toString()
          .substring(0, limit);
    }

    public String read(int start, int stop) {
        return this.getContent()
          .toString()
          .substring(start, stop);
    }
}

コードのコンパイル中、コンパイラは

read

メソッドのすべての呼び出しが、上で定義した3つのメソッドのうちの少なくとも1つに対応することを確認します。


3動的ポリモーフィズム

動的多態性では、Java仮想マシン(JVM)はサブクラスがその親形式に割り当てられたときに実行する適切なメソッドの検出を処理します。サブクラスは、親クラスで定義されているメソッドの一部または全部をオーバーライドする可能性があるため、これは必要です。

架空のファイルマネージャアプリケーションで、

GenericFile

というすべてのファイルの親クラスを定義しましょう。

public class GenericFile {
    private String name;

   //...

    public String getFileInfo() {
        return "Generic File Impl";
    }
}


GenericFile

を拡張しながら

getFileInfo()

メソッドをオーバーライドし、さらに情報を追加する

ImageFile

クラスを実装することもできます。

public class ImageFile extends GenericFile {
    private int height;
    private int width;

   //... getters and setters

    public String getFileInfo() {
        return "Image File Impl";
    }
}


ImageFile

のインスタンスを作成し、それを

GenericFile

クラスに割り当てると、暗黙的キャストが行われます。ただし、JVMは

ImageFile

の実際の形式への参照を保持しています。

  • 上記の構文はメソッドのオーバーライドに似ています**

    getFileInfo()

    メソッドを呼び出すことでこれを確認できます。

public static void main(String[]args) {
    GenericFile genericFile = new ImageFile("SampleImageFile", 200, 100,
      new BufferedImage(100, 200, BufferedImage.TYPE__INT__RGB)
      .toString()
      .getBytes(), "v1.0.0");
    logger.info("File Info: \n" + genericFile.getFileInfo());
}

予想通り、

genericFile.getFileInfo()

は、以下の出力に示すように

ImageFile

クラスの

getFileInfo()

メソッドをトリガーします。

File Info:
Image File Impl


4 Java

のその他の多態性

Javaにおけるこれら2つの主なタイプの多態性に加えて、Javaプログラミング言語には、多態性を示す他の特性があります。これらの特性のいくつかについて説明しましょう。


4.1. 強制

多型強制は、型エラーを防ぐためにコンパイラによって行われる暗黙の型変換を扱います。典型的な例は整数と文字列の連結で見られます:

String str = “string” + 2;


4.2. オペレータの過負荷

演算子またはメソッドのオーバーロードは、コンテキストに応じて異なる意味(形式)を持つ同じシンボルまたは演算子の多形特性を指します。

たとえば、プラス記号(+)は、

String

連結と同様に数学的加算にも使用できます。いずれの場合も、文脈のみ(すなわち、

引数の型)はシンボルの解釈を決定します。

String str = "2" + 2;
int sum = 2 + 2;
System.out.printf(" str = %s\n sum = %d\n", str, sum);

出力:

str = 22
sum = 4


4.3. 多相パラメータ

パラメトリック多態性を使用すると、クラス内のパラメータまたはメソッドの名前をさまざまな型に関連付けることができます。以下に典型的な例を示します。ここでは、

content



String

として定義し、後で

Integer

として定義します。

public class TextFile extends GenericFile {
    private String content;

    public String setContentDelimiter() {
        int content = 100;
        this.content = this.content + content;
    }
}

  • 多相パラメータの宣言は、

    変数隠蔽

    として知られる問題を引き起こす可能性があることに注意することも重要です。ここで、パラメータのローカル宣言は、同じ名前の別のパラメータのグローバル宣言を常にオーバーライドします。

この問題を解決するには、

this

キーワードなどのグローバル参照を使用してローカルコンテキスト内のグローバル変数を指すことをお勧めします。


4.4. 多型サブタイプ

多型サブタイプを使用すると、1つのタイプに複数のサブタイプを割り当て、そのタイプに対するすべての呼び出しによってサブタイプ内で使用可能な定義がトリガーされることを期待できます。

たとえば、

__GenericFile


sのコレクションがあり、それぞれに対して

getInfo()__メソッドを呼び出すと、コレクション内の各アイテムの派生元のサブタイプによって出力が異なることが予想されます。

GenericFile[]files = {new ImageFile("SampleImageFile", 200, 100,
  new BufferedImage(100, 200, BufferedImage.TYPE__INT__RGB).toString()
  .getBytes(), "v1.0.0"), new TextFile("SampleTextFile",
  "This is a sample text content", "v1.0.0")};

for (int i = 0; i < files.length; i++) {
    files[i].getInfo();
}

  • サブタイプ多型は


    アップキャストと遅延バインディング** の組み合わせによって可能になります。アップキャストには、スーパータイプからサブタイプへの継承階層のキャストが含まれます。

ImageFile imageFile = new ImageFile();
GenericFile file = imageFile;

上記の結果として生じる影響は、

__ImageFile –


固有のメソッドが新しいupcast

GenericFile__上で呼び出すことができないということです。ただし、サブタイプのメソッドは、スーパータイプで定義されている類似のメソッドをオーバーライドします。

スーパータイプへのアップキャスト時にサブタイプ固有のメソッドを呼び出すことができないという問題を解決するために、スーパータイプからサブタイプへの継承のダウンキャストを実行できます。これは以下によって行われます。

ImageFile imageFile = (ImageFile) file;

  • 遅延バインディング


    戦略は、コンパイラがアップキャスト後にどのメソッドをトリガーするかを解決するのに役立ちます** 。上記の例のi

    __mageFile#getInfo




    file#getInfo

    の場合、コンパイラは

    ImageFile

    s

    getInfo__メソッドへの参照を保持します。


5多態性に関する問題

適切にチェックしないとランタイムエラーを引き起こす可能性がある多態性のあいまいさをいくつか見てみましょう。


5.1. ダウンキャスト中の型識別

upcastを実行した後に、以前サブタイプ固有のメソッドへのアクセスを失ったことを思い出してください。私たちはこれをダウンキャストで解決することができましたが、これは実際の型チェックを保証するものではありません。

たとえば、アップキャストとその後のダウンキャストを実行したとします。

GenericFile file = new GenericFile();
ImageFile imageFile = (ImageFile) file;
System.out.println(imageFile.getHeight());

クラスは実際には

GenericFile

であり

ImageFile

ではありませんが、コンパイラは

GenericFile

から

ImageFile

へのダウンキャストを許可しています。

したがって、

imageFile

クラスで

getHeight()

メソッドを呼び出そうとすると、

GenericFile



getHeight()

メソッドを定義していないため、

ClassCastException

が発生します。

Exception in thread "main" java.lang.ClassCastException:
GenericFile cannot be cast to ImageFile

この問題を解決するために、JVMはランタイム型情報(RTTI)チェックを実行します。このように

instanceof

キーワードを使用して明示的な型識別を試すこともできます。

ImageFile imageFile;
if (file instanceof ImageFile) {
    imageFile = file;
}

上記は、実行時に

ClassCastException

例外を回避するのに役立ちます。

使用できるもう1つのオプションは、

try

および

catch

ブロック内でキャストをラップし、__ClassCastExceptionをキャッチすることです。

RTTIチェックは、タイプが正しいことを効果的に検証するために必要な時間とリソースのために、高価です。さらに、

instanceof

キーワードを頻繁に使用することは、デザインが悪いことを意味します。


5.2. 壊れやすい基本クラスの問題


https://en.wikipedia.org/wiki/Fragile


base

class[Wikipedia]によると、基本クラスへの一見安全な変更が派生クラスの誤動作を引き起こす可能性がある場合、基本クラスまたはスーパークラスは脆弱と見なされます。


GenericFile

というスーパークラスとそのサブクラス

TextFile

の宣言を考えてみましょう。

public class GenericFile {
    private String content;

    void writeContent(String content) {
        this.content = content;
    }
    void toString(String str) {
        str.toString();
    }
}

public class TextFile extends GenericFile {
    @Override
    void writeContent(String content) {
        toString(content);
    }
}


GenericFile

クラスを変更すると、

public class GenericFile {
   //...

    void toString(String str) {
        writeContent(str);
    }
}

上記の変更により、

writeContent()

メソッド内で

TextFile

が無限の再帰になり、最終的にスタックオーバーフローが発生します。

脆弱な基本クラスの問題に対処するために、サブクラスが

writeContent()

メソッドをオーバーライドしないように

final

キーワードを使用できます。

適切なドキュメンテーションも役に立ちます。そして最後に重要なことを言い忘れていましたが、一般的には継承よりも構成が優先されるべきです。


6. 結論

この記事では、長所と短所の両方に焦点を当てて、多態性の基本的な概念について説明しました。

いつものように、この記事のソースコードはhttps://github.com/eugenp/tutorials/tree/master/core-java-lang-oop[]にあります。