Informationen zur Bindung von Java-Methoden

Heute werde ich einen Artikel über Java-Methodenbindungen schreiben. Normalerweise lerne ich Java und war mir dessen nicht wirklich bewusst, daher werde ich es zu diesem Zeitpunkt richtig organisieren.

Vorherige Kenntniss

Lassen Sie uns die Besetzung als Vorwissen überprüfen. Casts haben explizite und implizite Typkonvertierungen. Weitere Informationen finden Sie unter der folgenden URL. https://techacademy.jp/magazine/28486 Sie sollten auch das Timing zur Kompilierungs- und Laufzeit kennen. Beim Kompilieren wird die von Ihnen geschriebene Java-Datei in Bytecode konvertiert. Die Ausführung ist der Zeitpunkt, zu dem die JVM den Bytecode tatsächlich ausführt. Wenn Sie in Eclipse entwickeln, schreiben Sie normalerweise den Code und er wird ohne Erlaubnis kompiliert. Der Code wird ausgeführt, wenn Sie auf die Schaltfläche Ausführen klicken. Kompilieren Sie beim Entwickeln in einem Terminal usw. mit dem Befehl javac, erstellen Sie eine Klassendatei und führen Sie sie tatsächlich mit dem Befehl java aus. Die im Artikel verwendeten Begriffe "binden" und "binden" haben alle die gleiche Bedeutung. (Es tut mir leid, dass es verwirrend ist ...)

Was ist überhaupt Methodenbindung?

Ich kannte nicht einmal die Bedeutung dieses Wortes, aber in einfachen Worten ist es wie ein Mechanismus in Java, der einen Methodenaufruf, die Signatur der aufgerufenen Methode und den Implementierungsteil verbindet (bindet). Die Signatur ist der Methodenname + Argument, und der Implementierungsteil ist die Verarbeitung in {}. Die Methode ist ein Klumpen dieser beiden, aber bitte denken Sie vorerst getrennt. Wenn Sie das nicht verstehen, können Sie an unerwarteten Orten stolpern (obwohl ich noch kein Ingenieur bin, kann ich nicht sagen, dass es großartig ist ...). Schauen wir es uns konkret an.

Animal.java


public class Animal {
      public void eat(double quantity) {
             System.out.println("Tiere" + quantity + "Tor.");
      }
}

Human.java


public class Human extends Animal {
      public void eat(int quantity) {
            System.out.println("Menschen sind" + quantity + "Tor.");
      }
}

Test.java


public class Test {
     public static void main(String[] args) {
           Animal animal = new Human();
           int quantity = 500;
           animal.eat(quantity);  // ?
     }
}

Angenommen, alle Klassen befinden sich im selben Paket. Was wird ausgegeben, wenn ich Test.java hier ausführe? Und warum?

Methodenbindung

Die richtige Antwort lautet "Das Tier hat 500,0 g gegessen." Warum. Zuerst dachte ich, dass Human als Referenz zugewiesen wurde, daher sollte die Ausgabe "Human ate 500g" sein. Der int-Typ wird auch als Argument in der Testklasse übergeben. In Wirklichkeit war die Antwort jedoch anders. Schauen wir uns an, wann in Java Methodenbindungen auftreten, um zu verstehen, was passiert ist. Siehe die folgende Tabelle.

Methodentyp Bindung zur Kompilierungszeit Bindung zur Laufzeit
non-statische Methode Methodensignatur Methodenimplementierung
statische Methode Methodensignatur, Methodenimplementierung

Da es sich bei eat diesmal um eine nicht statische Methode handelt, werde ich dies anhand dieser Erklärung erläutern (die Idee gilt auch für statische Methoden).

Bindung zur Kompilierungszeit

Java ordnet die aufgerufene Methode zur Kompilierungszeit ihrer Signatur zu. Mit anderen Worten kann gesagt werden, dass der Compiler die Signatur der jedes Mal aufgerufenen Methode bestimmt. Betrachten Sie diese Regel zusammen mit dem Code, den wir gerade betrachten. Die letzte Zeile von Test.java sieht folgendermaßen aus:

Test.java


animal.eat(quantity);

Zunächst untersucht der Compiler den Deklarationstyp (Typ) des variablen Tieres. Die Art des Tieres ist Tierart. Der Compiler beginnt dann mit der Suche: "Ja, ja. Diese Variable ist der Typ der Animal-Klasse. Lassen Sie uns herausfinden, ob es in dieser Klasse eine Methode gibt, die gerade aufgerufen wird." Zu diesem Zeitpunkt umfasst der Suchbereich auch Methoden, die mit der aufgerufenen Methode kompatibel sind. Eigentlich in der Tierklasse

Animal.java


eat(double quantity)

Die Signatur ist definiert. Im Aufrufer wird der int-Typ an das Argument übergeben, aber die Konvertierung von int in double erfolgt willkürlich (ohne explizites Casting), sodass der Compiler sagt: "Dies ist die Methode, die jetzt aufgerufen wird. Es ist eine mit "kompatible Methode" und verbindet den Methodenaufruf von animal.eat (Quantität) mit der eat-Methode (Signatur), die den doppelten Typ als Argument verwendet. Zu diesem Zeitpunkt hat der Compiler festgestellt, dass das Argument eat vom Typ double ist. Und es kann zur Laufzeit nicht mehr geändert werden. Lassen Sie es uns tatsächlich überprüfen.

Compiled from "Test.java"
public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class Human
       3: dup
       4: invokespecial #3                  // Method Human."<init>":()V
       7: astore_1
       8: sipush        500
      11: istore_2
      12: aload_1
      13: iload_2
      14: i2d
      15: invokevirtual #4                  // Method Animal.eat:(D)V
      18: return
}

Es kompiliert jede Datei mit dem Befehl javac vom Terminal und überprüft dann den Inhalt des kompilierten Codes mit javap -c Test. Hier sind die Zeilen, die Sie beachten sollten.

15: invokevirtual #4                  // Method Animal.eat:(D)V

Dies entspricht dem Methodenaufrufteil in der Testklasse animal.eat (Quantität). (D) gibt an, dass das Argument vom Doppeltyp ist, und V gibt an, dass der Rückgabewert ungültig ist. invokevirtual bedeutet, dass die tatsächliche Implementierung zur Laufzeit festgelegt wird. Der Code wird zur Laufzeit gemäß den Anweisungen dieses Bytecodes ausgeführt. Mit anderen Worten, im Compiler wird, wie oben beschrieben, der Inhalt des Prozesses von der JVM zur Laufzeit bestimmt, indem er einfach mit "Die aufgerufene Methode ist die Animal-Eat-Methode" verknüpft wird.

Laufzeitbindung

Alles, was Sie tun müssen, ist die animal.eat (Quantity) -Methode gemäß den Anweisungen des Bytecodes auszuführen. Vorhin habe ich gesagt, dass der Compiler den Deklarationstyp des variablen Tieres untersucht. Die JVM startet die Suche nach dem zugewiesenen Objekt. Mit anderen Worten

Test.java


Animal animal = new Human();

Der Compiler betrachtet die linke Seite (Animal) dieses Ausdrucks für eine Reihe von Bindungen, während die JVM zuerst das Objekt auf der rechten Seite (Human) betrachtet. Da dies ein Objekt der menschlichen Klasse ist, versucht es, die Methode des Essens zu finden: (D) V in der menschlichen Klasse. Andererseits kann keine Übereinstimmungsmethode gefunden werden, da die Klasse Mensch essen enthält: (I) V (I ist ein int-Typ). Daher sucht die JVM aus der Vererbungsbeziehung nach der entsprechenden Methode. Mit anderen Worten, wenn Sie sich die übergeordnete Klasse ansehen, die die Klasse erbt, und wenn sie nicht vorhanden ist, sehen Sie die übergeordnete Klasse weiter und so weiter, indem Sie die Hierarchie nach oben gehen und nach Essen suchen: (D) V. In diesem Fall gab es eine entsprechende Methode in der Animal-Klasse, die eine Ebene höher war, sodass der Implementierungsteil in dieser an die aufrufende Methode (animal.eat (Quantität)) gebunden ist und die Verarbeitung ausgeführt wird. Als Ergebnis war die Ausgabe "Das Tier aß 500,0 g".

Beispiel für eine Methodenbindung

Schauen wir uns zum Schluss ein Beispiel für die Methodenbindung an.

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
System.out.println(list); // [1, 2, 3, 4]

list.remove(3);
System.out.println(list); // [1, 2, 3]

Die Methode zum Entfernen des Listentyps ist mit zwei Methoden überladen, die unterschiedliche Argumenttypen verwenden: List.remove (int index) und List.remove (Objekt o) (https://docs.oracle.com/javase). /jp/8/docs/api/java/util/List.html). Hier hat remove ein Argument vom Typ int, sodass list.remove (3) und List.remove (int index) zur Kompilierungszeit gebunden sind. Zur Laufzeit ist die Verarbeitung (Implementierungsteil) in ArrayList.remove (int index) an list.remove (3) gebunden. Infolgedessen wird die 4 im dritten Index dieser Liste entfernt und als [1, 2, 3] angezeigt. Bisher hat sich nichts geändert. Aber was ist mit dem folgenden Beispiel?

Collection<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
System.out.println(list); // [1, 2, 3, 4]

list.remove(3);
System.out.println(list); // [1, 2, 4] ← ??

Das einzige, was sich geändert hat, ist, dass der Listencontainer von einem Listentyp in einen Sammlungstyp geändert wurde. Das Ergebnis wird als [1, 2, 4] angezeigt. Die Nummer 3 selbst wurde entfernt, nicht die dritte im Index. Dies liegt daran, dass Collection nur eine Entfernungsmethode hat, Collection.remove (Object o). Der Compiler bindet list.remove (3) mit der Methode remove, die diesen Objekttyp als Argument verwendet. Infolgedessen wird das Argument 3 in list.remove (3) als Objekt und nicht als Index behandelt. Wenn Sie es wie zuvor mit dem Befehl javap überprüfen, wird es als Collection.remove angezeigt: (Ljava / lang / Object;) Z (Z ist ein boolescher Typ). Dann wird ArrayList.remove (Objekt o) gemäß den Bytecode-Anweisungen jetzt von der JVM anstelle von ArrayList.remove (int index) ausgeführt. Als Ergebnis wurde [1, 2, 4] auf dem Bildschirm angezeigt. Wenn Sie nichts über Methodenbindung wussten, haben Sie möglicherweise mit der Annahme programmiert, dass der dritte Index normalerweise gelöscht wird. In diesem Fall tritt ein Fehler auf. Ich dachte, dass es ein großer Vorteil wäre, diesen Mechanismus zu kennen, um ihn zu vermeiden, also schrieb ich diesen Artikel. Vielen Dank, dass Sie so weit gelesen haben.

Recommended Posts

Informationen zur Bindung von Java-Methoden
Informationen zu Aufteilungsmethoden (Java)
Java-Methode
Java (Methode)
[Java Silver] Über gleich Methode
Java-Methode
[Java] -Methode
[Java] -Methode
Über die Java-Schnittstelle
[Java] Über Arrays
Java8-Methodenreferenz
Etwas über Java
Wo ist mit Java?
Informationen zu Java-Funktionen
Über die Methode
[Java] forEach-Methode
Über Java-Threads
[Java] -Schnittstelle
Über die Java-Klasse
Informationen zu Java-Arrays
Über Java-Vererbung
Über Schnittstelle, Java-Schnittstelle
Java8-Methodenreferenz
[Java] Zufällige Methode
[Java] Split-Methode
Über Java Var
Über Java Literal
Informationen zu Java-Befehlen
Informationen zur Java-Protokollausgabe
Informationen zur Java-Funktionsschnittstelle
Über keinen Methodenfehler
Java, über zweidimensionales Array
Über die Klassenteilung (Java)
JAVA DB-Verbindungsmethode
Über [Java] [StreamAPI] allMatch ()
Informationen zur Java StringBuilder-Klasse
Java Learning 2 (Lernen Sie die Berechnungsmethode)
[Java] Über Singleton Class
Java-Lernnotiz (Methode)
[Java] Über anonyme Klassen
Java 8 studieren (siehe Methode)
[Java Silver] Informationen zur Initialisierung
Über die Längenmethode
Informationen zur Java-Array-Liste
Über Java-Polymorphismus super ()
Informationen zur Vererbung (Java Silver)
Java-Programmierung (Klassenmethode)
Informationen zur Java String-Klasse
Informationen zu Java-Zugriffsmodifikatoren
Über Java-Lambda-Ausdrücke
Informationen zu Java-Einstiegspunkten
Java 10 Docker-Unterstützung
Persönliche Zusammenfassung über Java
Über die Kartenmethode
Über die Ahnenmethode
[Java] Grundlegende Methodenhinweise