it-swarm.com.de

Kann den komplexen Polymorphismus nicht verstehen

Ich studiere CS und wir haben Fragen zu Polymorphismus, um die ich mich nicht kümmern kann. Hier ist ein Beispiel:

public class AA{
    public AA(){
        foo();
    }
    private void foo() {
        System.out.print("AA::foo ");
        goo();
    }
    public void goo(){
        System.out.print("AA::goo ");
    }
}

public class BB extends AA{
    public BB(){
        foo();
    }
    public void foo(){
        System.out.print("BB:foo ");
    }
    public void goo(){
        System.out.print("BB::goo ");
    }
    public static void main(String[] args){
        // Code goes here
    }
}

Wenn nicht in main, füge ich die Zeile hinzu:

AA a = new BB();

es geht zuerst AA Konstruktor druckt AA: foo aber dann goo () schickt es zu BB's goo, warum so?

Ein einfacher Polymorphismus wie "Tier -> Katze/Spinne/Hund" ist leicht zu verstehen, aber wenn ich es so sehe, bin ich einfach verloren. Können Sie mir Tipps geben, wie Sie diesen Code lesen können? Was sind die Regeln?

EDIT: Es gibt keine @Override-Annotation, da dies eine Frage aus einer Prüfung ist.

16
Ron Kantor

Erläuterung

public class AA {

    private void foo() { ... }
    ^^^^^^^

}

Polymorphismus wird nicht auf private-Methoden angewendet. Eine Unterklasse erbt keine private-Methoden, daher können sie nicht überschrieben werden:

Eine Klasse C erbt von ihrer direkten Oberklasse alle konkreten Methoden m (sowohl statisch als auch Instanz) der Oberklasse, für die alle der folgenden erfüllt sind:

  • m ist ein Mitglied der direkten Oberklasse von C.
  • m ist public, protected oder wird mit Paketzugriff in demselben Paket wie C deklariert.
  • Keine in C deklarierte Methode hat eine Signatur, die eine Subsignature der Signatur von m ist.

Java-Sprachspezifikation - 8.4.8. Vererbung, Überschreiben und Ausblenden

Daher ruft der foo()-Aufruf des A-Konstruktors BB#foo nicht auf, sondern AA#foo

Der Aufruf von goo() in AA#foo bezieht sich jedoch auf die überschriebene Methode BB#goo. Hier wurden mit public-Methoden Methodenüberschreibung und Polymorphie angewendet.


Es ist ein bisschen schwierig, also würde ich empfehlen, dass Sie die @OverrideAnmerkung überall dort einfügen, wo sie sein soll.

public class BB extends AA {

    @Override   // it doesn't compile - no overriding here
    public void foo() { ... }
    @Override   // it does override
    public void goo() { ... }

}

Es kann auch hilfreich sein, ein anderes Problem zu erkennen:

Programmierer gelegentlich Überladung eine Methodendeklaration, wenn sie override bedeuten, was zu subtilen Problemen führt. Der Annotationstyp Override unterstützt die frühzeitige Erkennung solcher Probleme.

Wenn eine Methodendeklaration im Typ T mit @Override kommentiert wird, die Methode jedoch nicht von T eine in einem Supertyp von T deklarierte Methode überschreibt oder nicht einer öffentlichen Methode von Object überschrieben wird, tritt ein Fehler bei der Kompilierung auf.

/ - Java-Sprachspezifikation - 9.6.4.4. @Override

Illustration

Wenn ein Konstruktorkörper der nicht mit einem expliziten Konstruktoraufruf beginnt und der deklarierte Konstruktor nicht Teil der ursprünglichen Klasse Object ist}, dann beginnt der Konstruktorkörper implizit mit einem Superklassenkonstruktoraufruf super();, einem Aufruf des Konstruktors seiner direkten Oberklasse, die keine Argumente braucht.

Java-Sprachspezifikation - 8.8.7. Konstruktorkörper

Einfach gesagt,

public BB() {
    foo();
}

verwandelt sich in

public BB() {
    super();
    foo();
}

Im Hinblick auf super(); können wir die nächste Abbildung machen:

new BB()
        AA()                       // super(); -> AA constructor
                A#foo()            // private method call 
                        B#goo()    // polymorphic method call
        BB()                       // BB constructor
                B#foo()            // plain method call 
15
Andrew Tobilko

In den offiziellen Dokumenten wird das sehr gut erklärt:

https://docs.Oracle.com/javase/tutorial/Java/IandI/super.html

Wenn ein Konstruktor einen Superklassenkonstruktor nicht explizit aufruft, fügt der Java-Compiler automatisch einen Aufruf des Konstruktors ohne Argumente der Superklasse ein. Wenn die Superklasse keinen Konstruktor ohne Argumente enthält, wird ein Fehler bei der Kompilierung angezeigt. Object verfügt über einen solchen Konstruktor. Wenn also Object die einzige Superklasse ist, gibt es kein Problem.

Der Java-Compiler fügt also super () ohne Argumente für Sie hinzu.

Wenn die Klasse, die Sie erweitern, keinen Standardkonstruktor hat, müssen Sie diesen Konstruktor vorher mit args aufrufen.


Andernfalls wird AA:goo nicht aufgerufen, weil BB überschrieben wird, auch wenn @Override nicht angegeben ist. Wenn Sie diesen Aufruf sehen möchten, müssen Sie super () verwenden. in deiner b: goo Methode. Tatsächlich ist das Foo nicht überschrieben, weil es privat ist. Daher ist es nicht möglich, es zu überschreiben. Wenn Sie versuchen, die Anmerkung @Override hinzuzufügen, haben Sie einen Kompilierungsfehler.

3
Pau

Es gibt auch einen schwerwiegenden Konstruktionsfehler im Beispielcode: Er ruft eine überschreibbare Methode von einem Konstruktor aus auf. Dies bedeutet, dass das Objekt BB möglicherweise nicht vollständig initialisiert wird, wenn diese Methode für es aufgerufen wird. Zum Beispiel:

public class AA{

    public AA(){
        foo();
    }

    private void foo() {
        System.out.print("AA::foo ");
        goo();
    }

    public void goo(){
        System.out.print("AA::goo ");
    }
}

public class BB extends AA{

    private Date timestamp;

    public BB() {
        super();
        foo();
        timestamp = new Date();
    } 

    public void foo() {
        System.out.print("BB:foo ");
    }

    public void goo() {
        // goo() gets called before timestamp is initialized 
        // causing a NullPointerException
        System.out.print("BB::goo " + timestamp.getYear());
    }

    public static void main(String[] args){
        AA obj = new BB();
    }
}

Denken Sie daran: RUFEN SIE NIEMALS EINE ÜBERGREIFENDE METHODE VON EINEM KONSTRUKTOR AN

1
Adriaan Koster

AA :: foo () ist privat, daher sind AA :: foo () und BB :: foo () nicht dasselbe.

new BB()

ruft zuerst AA :: Constructor auf und ruft AA :: foo () auf.

AA :: foo () rufen Sie goo () auf und da Sie die BB-Klasse instanziiert haben, ist es BB :: goo ().

Bitte verwenden Sie das "override" Schlüsselwort für die Methode, wenn Sie so etwas tun möchten

1
Magnas

Polymorphismus ist einfach, aber manchmal verwirrend, wenn andere Namen verwendet werden [was hier der Fall ist].

Polymorphismus ist im Grunde eine Eltern-Kind-Beziehung. Der Schlüssel ist hier: Wenn Sie sich bemühen, die Namen der Klassen zu platzieren, verwenden Sie stattdessen selbst d. H. Geben Sie neben den Klassennamen eine Kommentarzeile ein (siehe unten)

public class AA{} //your Parent name
public class BB extends AA{} // yourself i.e. your name

Wenn es um den Code wie folgt geht, AA a = new BB();, dekodieren Sie den Code wie folgt:

BB bist du, AA ist dein Elternteil.

Das new-Schlüsselwort befindet sich bei YOU (d. h. BB), sodass ein neues Objekt von YOU erstellt oder geboren wird. Damit DU geboren werden kannst, ohne deine Eltern (d. H. AA), kannst du nicht existieren und so werden sie zuerst geboren oder erstellt (d. H. AA-Konstruktor würde ausgeführt). Sobald Ihre Eltern (d. H. AA) erstellt wurden, ist es Zeit für SIE, geboren zu werden (d. H. Der BB-Konstruktor würde ausgeführt).

In Ihrem Beispiel 

public AA(){
        foo(); -- line A
    }
    private void foo() {
        System.out.print("AA::foo ");
        goo(); -- line B
    }
    public void goo(){
        System.out.print("AA::goo "); -- line C
    }

Wie ich bereits sagte, würde Line A aufgerufen werden, wenn Sie AA a = new BB(); sagen, wie Line A sich im Konstruktor von AA befindet, Line A die foo () -Methode aufruft und die Steuerung in foo ( ), druckt "AA :: foo" und führt Zeile B aus. Zeile B ruft die goo () -Methode auf und so erreicht sie Line C . Nachdem Zeile C ausgeführt wurde, ist im AA-Konstruktor nichts mehr auszuführen (dh das Objekt wird erstellt), und die Steuerung geht zum untergeordneten Konstruktor über. (Wenn das übergeordnete Element erstellt wird, ist es Zeit, dass das Kind geboren wird.) und so würde der untergeordnete Konstruktor als nächstes aufgerufen.

Für Schüler/Anfänger empfehle ich dringend die Head First Java Edition. Es hilft Ihnen wirklich bei der Verlegung der Java Foundation Strong.

0
Jayanth