ホームページ  >  記事  >  Java  >  Java多態性オブジェクトの型変換と動的バインディングの詳細な説明

Java多態性オブジェクトの型変換と動的バインディングの詳細な説明

高洛峰
高洛峰オリジナル
2017-01-19 13:45:261332ブラウズ

Java ポリモーフィック オブジェクトの型変換
ここで説明するオブジェクト型変換は、任意の型のオブジェクトではなく、継承関係を持つオブジェクトを指します。継承関係のないオブジェクトをキャストすると、Java ランタイムは java.lang.ClassCastException 例外をスローします。

継承チェーンにおいて、サブクラスから親クラスへの変換を「上方変換」と呼び、親クラスからサブクラスへの変換を「ダウンキャスト」と呼びます。

変数を親クラスの型として定義することがよくありますが、このプロセスは上方変換です。プログラムの実行中、サブクラス メソッドの呼び出しは動的バインディング (ポリモーフィズム) を通じて実装されます。

ただし、場合によっては、親クラスにはない機能を完成させるために、上向き変換されたサブクラスのオブジェクトをサブクラスに変換し、サブクラスのメソッドを呼び出す必要があります。これが下向き変換です。

注: 親クラスのオブジェクトをサブクラス型に直接強制することはできません。アップキャストされたサブクラス オブジェクトをサブクラス型に再度変換することのみが可能です。つまり、サブクラス オブジェクトは、下方に変換する前に上方に変換する必要があります。以下のコードを見てください:

public class Demo {
  public static void main(String args[]) {
    SuperClass superObj = new SuperClass();
    SonClass sonObj = new SonClass();
    // 下面的代码运行时会抛出异常,不能将父类对象直接转换为子类类型
    // SonClass sonObj2 = (SonClass)superObj;
    // 先向上转型,再向下转型
    superObj = sonObj;
    SonClass sonObj1 = (SonClass)superObj;
  }
}
class SuperClass{ }
class SonClass extends SuperClass{ }

7 行目のコメントを削除してください。実行時に例外がスローされますが、コンパイルは成功します。

下方変換にはリスクがあるため、親クラスから参照を受け取るときは、必ずinstanceof演算子を使用して、オブジェクトが必要なサブクラスであるかどうかを判断してください。次のコードを参照してください:

public class Demo {
  public static void main(String args[]) {
    SuperClass superObj = new SuperClass();
    SonClass sonObj = new SonClass();
    // superObj 不是 SonClass 类的实例
    if(superObj instanceof SonClass){
      SonClass sonObj1 = (SonClass)superObj;
    }else{
      System.out.println("①不能转换");
    }
    superObj = sonObj;
    // superObj 是 SonClass 类的实例
    if(superObj instanceof SonClass){
      SonClass sonObj2 = (SonClass)superObj;
    }else{
      System.out.println("②不能转换");
    }
  }
}
class SuperClass{ }
class SonClass extends SuperClass{ }

実行結果:

①不能转换

概要: オブジェクトの型変換は、プログラムの実行時に自動的に実行されます。ダウンキャストされたオブジェクトは、現在の参照型のサブクラスである必要があります。

Java ポリモーフィズムと動的バインディング
Java では、親クラスの変数は、親クラスのインスタンスまたはサブクラスのインスタンスを参照できます。

最初にコードを読んでください:

public class Demo {
  public static void main(String[] args){
    Animal obj = new Animal();
    obj.cry();
    obj = new Cat();
    obj.cry();
    obj = new Dog();
    obj.cry();
  }
}
class Animal{
  // 动物的叫声
  public void cry(){
    System.out.println("不知道怎么叫");
  }
   
}
class Cat extends Animal{
  // 猫的叫声
  public void cry(){
    System.out.println("喵喵~");
  }
}
class Dog extends Animal{
  // 狗的叫声
  public void cry(){
    System.out.println("汪汪~");
  }
}

実行結果:

不知道怎么叫
喵喵~
汪汪~

上記のコードは、Animal、Cat、Dog という 3 つのクラスを定義しています。 Cat クラスと Dog クラスはどちらも Animal クラスを継承しています。 obj 変数は Animal 型で、Animal クラスのインスタンス、または Cat クラスと Dog クラスのインスタンスのいずれかを指すことができます。これは正しいことです。つまり、親クラスの変数は、親クラスのインスタンスまたはサブクラスのインスタンスを参照できます。すべての猫は動物ですが、すべての動物が猫であるわけではないため、その逆は間違っていることに注意してください。

obj は人間、猫、犬のいずれかであることがわかります。これはポリモーフィズムと呼ばれます。多態性とは、物事が異なる現れ方や形態を持つことを意味します。

もう一つの例は、「人間」です。TAには、運転手、教師、医者など、さまざまな表現や認識があります。自分を嫌いになると、「来世では新しい人になる」と言います。 、来世では運転手、教師、医者になることができます。「人間」には多態性があると言われます。

ポリモーフィズムが存在するために必要な 3 つの条件: 継承、オーバーライド、親クラス変数がサブクラス オブジェクトを参照する。

ポリモーフィズムを使用してメソッドを呼び出す場合:
まず親クラスにメソッドが存在するかどうかを確認し、存在しない場合はコンパイル エラーが発生し、サブクラスがメソッドをオーバーライドするかどうかを確認します。
サブクラスがこのメソッドをオーバーライドする場合は、サブクラスのメソッドを呼び出します。それ以外の場合は、親クラスのメソッドを呼び出します。

上記の例からわかるように、ポリモーフィズムの利点の 1 つは、サブクラスが多数ある場合、複数の変数を定義する必要がないことです。異なるサブクラスのインスタンスを参照するには、親クラス型の変数のみを定義できます。もう一度次の例を見てください:

public class Demo {
  public static void main(String[] args){
    // 借助多态,主人可以给很多动物喂食
    Master ma = new Master();
    ma.feed(new Animal(), new Food());
    ma.feed(new Cat(), new Fish());
    ma.feed(new Dog(), new Bone());
  }
}
// Animal类及其子类
class Animal{
  public void eat(Food f){
    System.out.println("我是一个小动物,正在吃" + f.getFood());
  }
}
class Cat extends Animal{
  public void eat(Food f){
    System.out.println("我是一只小猫咪,正在吃" + f.getFood());
  }
}
class Dog extends Animal{
  public void eat(Food f){
    System.out.println("我是一只狗狗,正在吃" + f.getFood());
  }
}
// Food及其子类
class Food{
  public String getFood(){
    return "事物";
  }
}
class Fish extends Food{
  public String getFood(){
    return "鱼";
  }
}
class Bone extends Food{
  public String getFood(){
    return "骨头";
  }
}
// Master类
class Master{
  public void feed(Animal an, Food f){
    an.eat(f);
  }
}

操作結果:

我是一个小动物,正在吃事物
我是一只小猫咪,正在吃鱼
我是一只狗狗,正在吃骨头

Master クラスの feed メソッドには、Animal type と Food type の 2 つのパラメータがあります。これは親クラスであるため、追加できます。マスタークラスが異なる動物に餌を与えるために複数のメソッドを必要としないように、サブクラスのインスタンスがそれに渡されます。
動的バインディング

ポリモーフィズムの性質を理解するために、Java でメソッドを呼び出す詳細なプロセスについて話しましょう。

1) コンパイラーは、オブジェクトの宣言された型とメソッド名を調べます。

obj.func(param) が呼び出され、obj が Cat クラスのオブジェクトであるとします。 func という名前の複数のメソッドが存在する可能性があることに注意してください。ただし、パラメータ シグネチャは異なります。たとえば、メソッド func(int) および func(String) が存在する場合があります。コンパイラは、Cat クラス内の func という名前のメソッドと、アクセス プロパティがパブリックであるその親クラス Animal 内の func という名前のメソッドをすべて列挙します。

このようにして、コンパイラーは呼び出される可能性のあるすべての候補メソッドのリストを取得します。

2) 次に、コンパイラーはメソッドの呼び出し時に提供されたパラメーターの署名をチェックします。

func という名前のすべてのメソッドの中に、指定されたパラメーターのシグネチャと正確に一致するメソッドがある場合、このメソッドが選択されます。このプロセスは、オーバーロード解決と呼ばれます。たとえば、func("hello") を呼び出すと、コンパイラは func(int) の代わりに func(String) を選択します。自動型変換の存在により、たとえば、呼び出しメソッドのパラメータと同じシグネチャを持つメソッドが見つからない場合は、型変換が実行され、存在する場合は検索が続行されます。最後に一致する型がないか、一致するメソッドが複数ある場合は、コンパイル エラーが発生します。

このようにして、コンパイラーは呼び出す必要があるメソッド名とパラメーターのシグネチャを取得します。

3) メソッドの修飾子が private、static、final (static とfinal については後で説明します)、またはコンストラクター メソッドの場合、コンパイラーはどのメソッドを呼び出す必要があるかを正確に認識します。これを静的バインディングと呼びます。

同様に、呼び出されるメソッドはオブジェクトの実際の型に依存し、実行時に動的にバインドされます。たとえば、 func("hello") が呼び出されると、エディターは動的バインディングを使用して func(String) を呼び出す命令を生成します。

4) プログラムの実行中に動的バインディングを使用してメソッドが呼び出される場合、JVM は確実に、obj によって参照されるオブジェクトの実際の型に最も適したクラスのメソッドを呼び出します。 obj の実際の型は、Animal のサブクラスである Cat であると仮定しています。Cat で func(String) が定義されている場合は、それが呼び出されます。そうでない場合は、Animal クラスとその親クラスで検索されます。

メソッドが呼び出されるたびに検索が必要になり、非常に時間がかかります。そのため、JVM は、すべてのメソッドの名前、パラメーター シグネチャ、クラスをリストしたメソッド テーブル (メソッド ラベル) をクラスごとに事前に作成します。このようにして、メソッドが実際に呼び出されるとき、仮想マシンはこのテーブルを検索するだけで済みます。上記の例では、JVM は Cat クラスのメソッド テーブルで func("hello") の呼び出しに一致するメソッドを検索します。このメソッドは Cat.func(String) または Animal.func(String) です。 super.func("hello") を呼び出すと、コンパイラは親クラスのメソッド テーブルを検索することに注意してください。

Animal クラスに、cry()、getName()、getAge() の 3 つのメソッドが含まれていると仮定すると、そのメソッド テーブルは次のようになります。 ; Animal.getName ()
getAge() -> Animal.getAge()

実際、Animal にはデフォルトの親クラス Object (後で説明します) もあり、Object のメソッドを継承します。は完全ではありません。

CatクラスがAnimalクラスのcry()メソッドをオーバーライドし、新しいメソッドclimTree()を追加すると、そのパラメータリストは次のようになります。 ; Animal.getName()

getAge() ->Animal.getAge()

climbTree() ->Cat.climbTree()

実行時の obj.cry() メソッドの呼び出しプロセスは次のとおりです。
JVM は最初に、obj の実際のタイプのメソッド テーブルにアクセスします。これは、Animal クラスのメソッド テーブル、または Cat クラスとそのサブクラスのメソッド テーブルである可能性があります。
JVM は、メソッド テーブル内で cry() に一致するメソッドを検索します。それが見つかると、それがどのクラスに属しているかがわかります。

JVM はこのメソッドを呼び出します。


Java ポリモーフィック オブジェクトの型変換と動的バインディングに関する詳細な記事については、PHP 中国語 Web サイトに注目してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。