Heim  >  Artikel  >  Java  >  Detaillierte Erläuterung der Typkonvertierung und dynamischen Bindung polymorpher Java-Objekte

Detaillierte Erläuterung der Typkonvertierung und dynamischen Bindung polymorpher Java-Objekte

高洛峰
高洛峰Original
2017-01-19 13:45:261296Durchsuche

Typkonvertierung polymorpher Java-Objekte
Die hier erwähnte Objekttypkonvertierung bezieht sich auf Objekte mit Vererbungsbeziehungen, nicht auf Objekte jeglichen Typs. Beim Umwandeln eines Objekts, das keine Vererbungsbeziehung hat, löst die Java-Laufzeitumgebung eine java.lang.ClassCastException-Ausnahme aus.

In der Vererbungskette nennen wir die Konvertierung einer Unterklasse in eine übergeordnete Klasse „Upcasting“, und die Konvertierung einer übergeordneten Klasse in eine untergeordnete Klasse wird „Downcasting“ genannt.

Oft definieren wir Variablen als den Typ der übergeordneten Klasse, beziehen uns jedoch auf das Objekt der Unterklasse. Dieser Prozess ist eine Aufwärtstransformation. Wenn das Programm ausgeführt wird, wird der Aufruf von Unterklassenmethoden durch dynamische Bindung implementiert, bei der es sich um Polymorphismus handelt.

Um jedoch einige Funktionen zu vervollständigen, die die übergeordnete Klasse nicht hat, müssen wir das Unterklassenobjekt nach der Aufwärtstransformation in eine Unterklasse konvertieren und die Methode der Unterklasse aufrufen. Dies ist eine Abwärtstransformation.

Hinweis: Sie können das Objekt der übergeordneten Klasse nicht direkt in den Unterklassentyp umwandeln. Sie können nur das Upcast-Unterklassenobjekt erneut in den Unterklassentyp konvertieren. Mit anderen Worten: Unterklassenobjekte müssen nach oben transformiert werden, bevor sie nach unten transformiert werden können. Bitte schauen Sie sich den folgenden Code an:

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

Entfernen Sie den Kommentar in Zeile 7. Beim Ausführen wird eine Ausnahme ausgelöst, aber die Kompilierung kann erfolgreich sein.

Da beim Downcasting Risiken bestehen, sollten Sie beim Empfang einer Referenz von der übergeordneten Klasse unbedingt den Instanzoperator verwenden, um festzustellen, ob es sich bei dem Objekt um die gewünschte Unterklasse handelt:

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

Ausführungsergebnis:

①不能转换

Zusammenfassung: Die Typkonvertierung des Objekts wird beim Ausführen des Programms automatisch durchgeführt Eine Unterklasse des aktuellen Referenztyps.

Java-Polymorphismus und dynamische Bindung
In Java können Variablen einer übergeordneten Klasse auf Instanzen der übergeordneten Klasse oder Instanzen der Unterklasse verweisen.

Bitte lesen Sie zuerst einen Code:

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("汪汪~");
  }
}

Laufendes Ergebnis:

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

Der obige Code Es sind drei Klassen definiert, nämlich Animal, Cat und Dog. Sowohl die Cat- als auch die Dog-Klasse erben von der Animal-Klasse. Die obj-Variable ist vom Typ Animal und kann entweder auf eine Instanz der Animal-Klasse oder auf eine Instanz der Cat- und Dog-Klassen verweisen, was korrekt ist. Mit anderen Worten: Variablen der übergeordneten Klasse können auf Instanzen der übergeordneten Klasse oder auf Instanzen der Unterklasse verweisen. Beachten Sie, dass das Gegenteil falsch ist, da alle Katzen Tiere sind, aber nicht alle Tiere Katzen.

Es ist ersichtlich, dass obj ein Mensch, eine Katze oder ein Hund sein kann. Es hat verschiedene Ausdrücke, was als Polymorphismus bezeichnet wird. Polymorphismus bedeutet, dass eine Sache unterschiedliche Erscheinungsformen oder Formen hat.

Ein weiteres Beispiel ist „Mensch“. Es gibt viele verschiedene Ausdrücke oder Erkenntnisse, die ein Fahrer, Lehrer, Arzt usw. sein können. Wenn Sie sich selbst hassen, werden Sie sagen: „Werden Sie ein neuer Mensch.“ Leben“, dann wirst du im nächsten Leben zum Fahrer oder Lehrer. Ärzte können das, wir sagen einfach, dass „Menschen“ Polymorphismus haben.

Es gibt drei notwendige Bedingungen für das Vorhandensein von Polymorphismus: Vererbung, Überschreibung und übergeordnete Klassenvariablen beziehen sich auf Unterklassenobjekte.

Beim Aufruf einer Methode mit Polymorphismus:
Überprüfen Sie zunächst, ob die Methode in der übergeordneten Klasse vorhanden ist. Wenn dies nicht der Fall ist, prüfen Sie, ob die Unterklasse die Methode überschreibt.
Wenn die Unterklasse diese Methode überschreibt, rufen Sie die Methode der Unterklasse auf, andernfalls rufen Sie die Methode der übergeordneten Klasse auf.

Wie aus dem obigen Beispiel ersichtlich ist, besteht ein Vorteil des Polymorphismus darin, dass bei vielen Unterklassen nicht mehrere Variablen definiert werden müssen. Sie können nur eine Variable des übergeordneten Klassentyps definieren, auf die verwiesen werden soll verschiedene Unterklassen. Schauen Sie sich bitte noch einmal das folgende Beispiel an:

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

Laufergebnis:

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

Die Feed-Methode der Meisterklasse hat zwei Parameter sind Tiertyp und Lebensmitteltyp. Da es sich um übergeordnete Klassen handelt, können Instanzen von Unterklassen an sie übergeben werden, sodass die Master-Klasse nicht mehrere Methoden zum Füttern verschiedener Tiere benötigt.
Dynamische Bindung

Um die Natur des Polymorphismus zu verstehen, sprechen wir über den detaillierten Prozess des Methodenaufrufs in Java.

1) Der Compiler prüft den deklarierten Typ und Methodennamen des Objekts.

Angenommen, obj.func(param) wird aufgerufen und obj ist ein Objekt der Cat-Klasse. Es ist zu beachten, dass es möglicherweise mehrere Methoden mit dem Namen func, jedoch mit unterschiedlichen Parametersignaturen gibt. Beispielsweise könnte es die Methoden func(int) und func(String) geben. Der Compiler zählt alle Methoden mit dem Namen func in der Cat-Klasse und die Methoden mit dem Namen func in ihrer übergeordneten Klasse Animal auf, deren Zugriffseigenschaften öffentlich sind.

Auf diese Weise erhält der Compiler eine Liste aller Kandidatenmethoden, die aufgerufen werden können.

2) Als nächstes prüft der Compiler die Parametersignaturen, die beim Aufruf der Methode bereitgestellt werden.

Wenn es unter allen Methoden mit dem Namen func eine gibt, die genau mit der angegebenen Parametersignatur übereinstimmt, wird diese Methode ausgewählt. Dieser Vorgang wird als Überladungsauflösung bezeichnet. Wenn Sie beispielsweise func("hello") aufrufen, wählt der Compiler func(String) anstelle von func(int). Aufgrund der Existenz einer automatischen Typkonvertierung kann int beispielsweise in double konvertiert werden. Wenn keine Methode mit derselben Signatur wie der aufrufende Methodenparameter gefunden wird, wird die Typkonvertierung durchgeführt und die Suche fortgesetzt Am Ende gibt es keinen passenden Typ oder es gibt mehrere passende Methoden. Dann liegt ein Kompilierungsfehler vor.

Auf diese Weise erhält der Compiler den Methodennamen und die Parametersignatur, die aufgerufen werden müssen.

3) Wenn der Modifikator der Methode privat, statisch, final (statisch und final wird später erklärt) oder eine Konstruktormethode ist, weiß der Compiler genau, welche Methode aufgerufen werden soll Dies wird als statische Bindung bezeichnet.

Entsprechend hängt die aufgerufene Methode vom tatsächlichen Typ des Objekts ab und wird zur Laufzeit dynamisch gebunden. Wenn beispielsweise func("hello") aufgerufen wird, generiert der Editor mithilfe der dynamischen Bindung eine Anweisung zum Aufrufen von func(String).

4) Wenn das Programm ausgeführt wird und eine Methode mithilfe der dynamischen Bindung aufgerufen wird, ruft die JVM definitiv die Methode der Klasse auf, die für den tatsächlichen Typ des von obj referenzierten Objekts am besten geeignet ist. Wir haben angenommen, dass der tatsächliche Typ von obj Cat ist, eine Unterklasse von Animal, und wenn func(String) in Cat definiert ist, wird es aufgerufen, andernfalls wird in der Animal-Klasse und ihrer übergeordneten Klasse danach gesucht.

Jedes Mal, wenn eine Methode aufgerufen wird, ist eine Suche erforderlich, die viel Zeit in Anspruch nimmt. Daher erstellt die JVM vorab eine Methodentabelle (Methodenbezeichnung) für jede Klasse, in der die Namen und Parametersignaturen aufgeführt sind und Die Klasse, zu der es gehört. Auf diese Weise muss die virtuelle Maschine beim tatsächlichen Aufruf der Methode nur diese Tabelle nachschlagen. Im obigen Beispiel durchsucht die JVM die Methodentabelle der Cat-Klasse nach einer Methode, die einem Aufruf von func("hello") entspricht. Diese Methode kann Cat.func(String) oder Animal.func(String) sein. Beachten Sie, dass der Compiler beim Aufruf von super.func("hello") die Methodentabelle der übergeordneten Klasse durchsucht.

Angenommen, die Animal-Klasse enthält drei Methoden: cry(), getName() und getAge(), dann lautet ihre Methodentabelle wie folgt:
cry() ->
getName() -> Animal.getName()
getAge() -> Animal.getAge()

Tatsächlich hat Animal auch ein Standard-Elternklassenobjekt (wird später erklärt). ), die Objektmethoden erben, sodass die oben aufgeführten Methoden nicht vollständig sind.

Angenommen, die Cat-Klasse deckt die Methode „cry()“ in der Klasse „Animal“ ab und fügt eine neue Methode „climbTree()“ hinzu. Dann lautet ihre Parameterliste:
cry() ->
getName() -> Animal.getName()
getAge() -> Animal.getAge()
climbTree() -> Cat.climbTree()

in When Beim Ausführen läuft der Aufruf der obj.cry()-Methode wie folgt ab:
JVM greift zunächst auf die Methodentabelle des tatsächlichen Typs von obj zu, bei der es sich um die Methodentabelle der Animal-Klasse oder die Methodentabelle der Klasse handeln kann Cat-Klasse und ihre Unterklassen.
Die JVM sucht in der Methodentabelle nach einer Methode, die zu „cry()“ passt. Sobald sie gefunden wurde, weiß sie, zu welcher Klasse sie gehört.
JVM ruft diese Methode auf.

Ausführlichere Artikel zur Typkonvertierung und dynamischen Bindung polymorpher Java-Objekte finden Sie auf der chinesischen PHP-Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn