Polymorphism

Kolodets

Mitglied
Es gibt die Regel: ein Objekt kann nur die Methoden und Variablen abrufen, die in seiner Referenz verfügbar sind.

Dann verstehe ich folgendes nicht: wie kann ein B-Objekt mit B-Referenz auf die Methode von A zugreifen?

class A {
public String methodeA(){return "A";}
}
public class B extends A {
public static void main(String[] args){
B b = new B();
Sytem.out.println(b.methodA())
}
}

Ausgabe is: "A"
 

httpdigest

Top Contributor
Die Klasse B erbt von der Klasse A. Somit ist jedes B auch ein A. Und ueber die Referenz des Objektes B ist deswegen ja auch die methodeA in A verfuegbar.
 

Blender3D

Top Contributor
Dann verstehe ich folgendes nicht: wie kann ein B-Objekt mit B-Referenz auf die Methode von A zugreifen?
Polymorphie bedeutet Mehrgestaltigkeit.
In deinem Beispiel kommt das noch nicht zur Anwendung.
Deine Klasse B erweitert die Klasse A und besitzt folglich auch die Methoden von A.
Damit haben wir hier es noch nicht mit Polymorphie zu tun sondern mit schlichter Vererbung.
Dass B also hier die geerbte Methode benutzen kann ist also verständlich.
Würde die Klasse B die Methode überschreiben hätten wir es mit Polymorphie zu tun. Dann würde im dem Fall aber auch die überschriebene Methode aufgerufen werden.

Beispiel;
Java:
public abstract class Tier {
    public abstract void gibLaut();
}
Java:
public class Ente extends Tier {
    @Override
    public void gibLaut() {
        System.out.println("quack");
    }
}
Java:
public class Kuh extends Tier {
    @Override
    public void gibLaut() {
        System.out.println("muh");
    }
}
Java:
public static void main(String[] args) {
        Tier tierA = new Kuh();
        Tier tierB = new Ente();
        tierA.gibLaut();
        tierB.gibLaut();  
    }
 

Kolodets

Mitglied
Die Klasse B erbt von der Klasse A. Somit ist jedes B auch ein A. Und ueber die Referenz des Objektes B ist deswegen ja auch die methodeA in A verfuegbar.
Dann reicht schlichtes Angucken der Referenz nicht aus, um die Verfügbarkeit zu beurteilen. Mann muss schon die Erbungen im Betracht ziehen. Damit kann Referenz B das gleiche wie Referenz A sein, was verfügbare Methoden/Variablen angeht
 

Kolodets

Mitglied
Polymorphie bedeutet Mehrgestaltigkeit.
In deinem Beispiel kommt das noch nicht zur Anwendung.
Deine Klasse B erweitert die Klasse A und besitzt folglich auch die Methoden von A.
Damit haben wir hier es noch nicht mit Polymorphie zu tun sondern mit schlichter Vererbung.
Dass B also hier die geerbte Methode benutzen kann ist also verständlich.
Würde die Klasse B die Methode überschreiben hätten wir es mit Polymorphie zu tun. Dann würde im dem Fall aber auch die überschriebene Methode aufgerufen werden.
Ich dachte, Polymorphie wäre Umschalten zwischen Referenz- und Objekttypen
also zum Beispiel statt B b = new B()
A b = new B() machen
Aber in Wirklichkeit ist hier das Überschreiben von Methoden involviert.
Danke für die Erklärung
 
Zuletzt bearbeitet:

KonradN

Super-Moderator
Mitarbeiter
Kann u.U. zum sog. "Diamond-problem" führen.
Also das Diamond-problem hat nichts mit Polymorphie zu tun! Das ist ein Problem der "Multiple Inheritance".

Polymorphie in Java findet sich in Java (gemäß JLS) nur als polymorphe Methoden, was auch im Wikipedia Artikel beschrieben wurde. Darüber hinaus gibt es in Java eigentlich nichts. (Es finden sich "polymorph methods" in Kapitel 15.12 ... 15.12.3 behandelt die Compile-Zeit Methodenfindung und 15.12.4 die Runtime. So ich mich jetzt nicht irre - Habe ich jetzt im Detail nicht heraus gesucht).

Das ist dann auch, was man so auf Webseiten findet, z.B.
 

White_Fox

Top Contributor
Ich dachte, Polymorphie wäre Umschalten zwischen Referenz- und Objekttypen
also zum Beispiel statt B b = new B()
A b = new B() machen
A b = new B() funktioniert deshalb, weil B auch A ist.
Du hast eine Klasse Landfahrzeuge und eine Klasse Pkw, wobei Pkw von Landfahrzeuge erbt. Dann ist Pkw natürlich ein Landfahrzeug.

Aber in Wirklichkeit ist hier das Überschreiben von Methoden involviert.
Versuche mal, die konkrete Implementation der Methode außer Acht zu lassen. Es gibt den Methodenvertrag, da wird geregelt was die Methode genau machen soll. Wenn die Klasse Landfahrzeuge eine Mehtode fahren(position ziel) bereitstellt und diese in der Klasse Pkw überschrieben wird, sollte Pkw nach der Spezifikation das Gleiche tun wie jedes andere Landfahrzeug. Und nicht etwa in der Zeit umherreisen.

PS: Was meinst du mit "Umschalten zwischen Referenzen und Objekttypen"? In Java arbeitest du ausschließlich auf Referenzen, was anderes gibt es nicht.
 

Kolodets

Mitglied
PS: Was meinst du mit "Umschalten zwischen Referenzen und Objekttypen"? In Java arbeitest du ausschließlich auf Referenzen, was anderes gibt es nicht.
(A) new B(); Objekttyp wird von B nach A umgeschaltet oder?oder ist das alles Referenz?
A b = new B() funktioniert deshalb, weil B auch A ist.
Du hast eine Klasse Landfahrzeuge und eine Klasse Pkw, wobei Pkw von Landfahrzeuge erbt. Dann ist Pkw natürlich ein Landfahrzeug.
Ein Küken erbt von Henne, ist aber jetzt und in Zukunft keine Henne wenn daraus ein Hahn wird. Eier legen kann ein Kücken auch nicht, wenn die eierLegen() Methode bei Henne privat ist und keine Vermittler-Methode hat
Versuche mal, die konkrete Implementation der Methode außer Acht zu lassen. Es gibt den Methodenvertrag, da wird geregelt was die Methode genau machen soll. Wenn die Klasse Landfahrzeuge eine Mehtode fahren(position ziel) bereitstellt und diese in der Klasse Pkw überschrieben wird, sollte Pkw nach der Spezifikation das Gleiche tun wie jedes andere Landfahrzeug. Und nicht etwa in der Zeit umherreisen.

PS: Was meinst du mit "Umschalten zwischen Referenzen und Objekttypen"? In Java arbeitest du ausschließlich auf Referenzen, was anderes gibt es nicht.
so wie ich es weiss, Verträge gibt es nur für die vordefinierten Systemmethoden, z.B. equals()
wenn ich Methode fahren(position ziel) definiere, dann hat diese keinen Vertrag. oder?
 
Zuletzt bearbeitet:

temi

Top Contributor
Ein Küken erbt von Henne, ist aber jetzt und in Zukunft keine Henne wenn daraus ein Hahn wird.
Vererbung entspricht immer einer "Ist-ein"-Beziehung. Wenn diese nicht zutrifft, dann wurde Vererbung falsch eingesetzt. Durch Vererbung wird eine Klasse erweitert ("extends") und nicht zu etwas völlig anderem.

Es ist hier nicht das Thema, aber Vererbung sollte man auch nicht überschätzen. Es gilt "Composition over Inheritance", also das man die Komposition der Vererbung vorziehen sollte. Dies aber nur am Rande.

wenn ich Methode fahren(position ziel) definiere, dann hat diese keinen Vertrag. oder?
Es gibt eine ganze Reihe von "Verträgen" (z. B. in Form von Interfaces), die bereits in der Standardbibliothek von Java enthalten sind. Du kannst aber natürlich auch eigene Schnittstellen (= Verträge) erstellen. Du gibst dann den Vertrag vor, an den du oder andere sich halten müssen. Das passiert in tausenden von externen Bibliotheken, die du nutzen kannst (oder selbst bereitstellst).
 
Zuletzt bearbeitet:

KonradN

Super-Moderator
Mitarbeiter
(A) new B(); Objekttyp wird von B nach A umgeschaltet oder?oder ist das alles Referenz?
So ein cast macht nichts mit dem Objekt. Das Objekt bleibt immer gleich!

Das kann man sich sehr gut bildlich vorstellen:
Du hast ein Gebäude. Dieses ist ein Einkaufszentrum. Das ist etwas festes und das verändert sich nicht.

Nun kannst Du Variablen haben, die darauf verweisen. Variablen haben einen Typ. Das kann man sich wie Zettel. mit der Adresse vorstellen und der Typ ist etwas, das auch drauf steht. Du kannst also ein Zettel haben:
"Einkaufszentrum: <Referenz, wo es zu finden ist>"
Dann ist klar: Da kannst Du einkaufen().
Du kannst aber auch einen Zettel haben:
"Gebäude: <Referenz, wo es zu finden ist>"

So ein Cast ist dann nur ein: Du hast einen Zettel "Gebäude: ..." und sagst: "Das ist ein Einkaufszentrum". Das kannst Du generell machen. Du kannst auch hin gehen und sagen: "Das ist ein Wohnhaus" - da kannst Du also einziehen(). Nur eben bekommst Du dann einen drüber: "Du Depp, das ist ein Einkaufszentrum und kein Wohnhaus"

Ein Küken erbt von Henne, ist aber jetzt und in Zukunft keine Henne wenn daraus ein Hahn wird. Eier legen kann ein Kücken auch nicht, wenn die eierLegen() Methode bei Henne privat ist und keine Vermittler-Methode hat
Das Beispiel passt so absolut nicht. Ein Objekt von einem Typ bleibt immer ein Objekt von diesem Typ. Aus einem Objekt vom Typ Küken kann also nie ein Objekt vom Typ Henne werden!

Du musst das also sowas immer genau betrachten. Das Küken ist ein Huhn. Du hast also eine Klasse Huhn und das hat dann ein Geschlecht und ein Alter. legeEi() hat also eine Prüfung: Wenn es männlich ist oder zu jung oder zu alt, dann geht es nicht und es kommt z.B. eine illegal state Exception. Oder es hat ein Verhalten, dass sich ändern kann. Dann hat man das als eigene Implementierung und dann hat ein Huhn eine Instanz von EierLegen Interface und dann gibt es davon mehrere Implementationen.
so wie ich es weiss, Verträge gibt es nur für die vordefinierten Systemmethoden, z.B. equals()
wenn ich Methode fahren(position ziel) definiere, dann hat diese keinen Vertrag. oder?
Jede Klasse oder Interface ist ein Vertrag. Es wird eine Schnittstelle definiert und dazu gesagt, was das Verhalten ist. Und daran muss man sich halten. Im bisherigen Thread ging es nur um die Schnittstelle - es gibt eine Methode boolean equals(Object). Aber dazu gehört auch eine Beschreibung des Verhaltens. Das ist für Java selbst egal. Das ist dann mehr eine Frage von Clean Code (SOLID Principles, speziell das L: Liskovs Substitution Principle)

Also Vertrag und Schnittstelle sind zwei Dinge und du redest vermutlich mehr von rein Schnittstelle. Für die Schnittstelle ist es so z.B. OK, bei equals immer true zurück zu geben und bei hashcode einfach eine Zufallszahl zu generieren um diese zurück zu geben. Das erfüllt die Schnittstelle. Aber es erfüllt nicht den Vertrag, denn wann man die Beschreibung von Object liest, dann findet man dort gewisse Vorgaben und die sind nicht erfüllt.
 

White_Fox

Top Contributor
(A) new B(); Objekttyp wird von B nach A umgeschaltet oder?oder ist das alles Referenz?
Wie bereits gesagt: Auch da arbeitest du nur auf Referenzen, der new-Operator liefert nur die Referenz.

A a = new B() ist nur eine künstliche Einschränkung, die manchmal sinnvoll sein kann. Nehmen wir mal folgendes an:

Java:
abstract class B extends A{
    abstract public void do();
}

Dann würde new B() Ärger vom Compiler zurückliefern, weil du B nicht instanzieren kannst, denn B ist ja abstrakt. Die Methode do() muß überschrieben werden. Nun kann es lauter Klassen geben, die do() in ähnlicher, aber anderer Weise überschreiben.

Java:
class B1 extends B{
    public void do(){
        System.out.println("Zzzzz...");
    }
}

class B2 extends B{
    public void do(){
        System.out.println("Schnarch...");
    }
}

class B2 extends B{
    public void do(){
        System.out.println("Grunz! Grunz!");
    }
}

Dann könntest du selbstverständlich schreiben: B b = new B3();. Dann kann sich b nur so verhalten, wie du B deklariert hast, und die Erweiterungen die B3 mitbringt, die siehst du nicht. Es ist einfach nur eine künstliche Beschränkung, die sinnvoll sein kann.

Du hast z.B. die besagte Klasse Landfahrzeuge, und die Methode fahren(). Jetzt bietet sich Landfahrzeuge als abstrakter Oberberiff an, die Klasse als abstract zu deklarieren, und dann später z.B. Panzer, Pkw, Kinderwagen davon abzuleiten. Oder Panzer ebenfalls abstrakt deklarieren und dann Leopard, Tiger, T-34, Abrams davon abzuleiten. Und fahren() kann dann auch abstrakt sein, da alle Klassen unterschiedliche Fahreigenschaften haben (auch wenn es da bessere Methoden gibt als die Methode für jede Klasse zu überschreiben, aber das wäre eine spätere Lektion). Mit der Deklaration hast du aber sichergestellt, daß jedes Landfahrzeug über eine Methode fahren() verfügt. Und dann geht sowas:

Java:
public abstract class Landfahrzeug{
    abstract fahren(Position ziel);
}

//...

void fahreEinkaufen(Landfahrzeug fahrzeug){
    //...
}
Und da ist es jetzt egal ob du mit dem Panzer oder dem Kinderwagen zum Einkaufen fährst, du kannst prinzipiell mit jedem Landfahrzeug zum Einkaufen fahren. Mit einem Objekt vom Typ Werkzeug oder Schusswaffe kannst du nicht zum Einkaufen fahren.
Das Ganze dient prinzipiell dazu, Ordnung und Struktur aufzubauen. Du definierst, welcher Objekttyp was ist und was Instanzen davon tun können. Ein Objekt vom Typ Antrieb (Gasturbine, Diesel, Ottomotor, E-Motor,...) ist kein Landfahrzeug, aber jedes Landfahrzeug hat einen Antrieb, deshalb wäre jede Vererbungsbeziehung zwischen Landfahrzeug und Antrieb Unsinn.


so wie ich es weiss, Verträge gibt es nur für die vordefinierten Systemmethoden, z.B. equals()
wenn ich Methode fahren(position ziel) definiere, dann hat diese keinen Vertrag. oder?
Wenn du festlegst, das Landfahrzeug eine Methode fahren() hat, dann legst du auch fest was diese Methode erreichen soll. Oder nicht? Das ist ein Vertrag, auch wenn du ihn festlegst. Du könntest natürlich auch sowas implementieren:
Java:
public class ByzantinischeKutsche extends Landfahrzeug{
    public fahren(Position ziel){
        macheZeitsprung();
    }
}

Der Compiler wird nicht meckern, aber dein Programm wird am Ende wunderliche Dinge tun. Der Compiler meckert nur Fehler an, die gegen die Sprachregeln verstoßen, ansonsten ist es dem Compiler völlig egal was dein Programm macht. Und deswegen ist es so wichtig, Code so zu schreiben, daß man lesen kann was er tut, und deshalb reitet Konrad so gerne auf Clean Code herum. Völlig zu recht, übrigens.
 

KonradN

Super-Moderator
Mitarbeiter
Ich denke schon, dass es auch Polymorphie betrifft.
Die Gebiete berühren sich - natürlich spielt beim "diamond problem" auch "polymorphie" mit hinein. Aber dennoch ist es kein Thema von Polymorphie selbst sondern eben von "multiple inheritance".


Und die Ursprüngliche Aussage zu Polymorphie war:
Kann u.U. zum sog. "Diamond-problem" führen.
Polymorphie selbst führt nicht zu dem diamond problem. Erst multiple inheritance führt dazu. Das ist auch der Grund, wieso das "diamond problem" bei "multiple inheritance" und nicht bei "polymorphie" bei Wikipedia zu finden ist (Links in #7). Und Sprachen mit Polymorphie aber ohne multiple Inheritance haben alle kein Diamond Problem.


Daher die ganz klare Abgrenzung in einem Thread, in dem ein Anfänger versucht, die Begriffe zu verstehen. Da ist es wichtig, dass man hier genau mit den Begriffen aufpasst um eben nichts zu vermischen. Vor allem da es hier um den Java Bereich geht (Kein Multiple Inheritance --> Kein Diamond Problem)!
 

Blender3D

Top Contributor
A: Das Diamanten-Problem ist ein Konflikt, der in der Vererbungshierarchie von Klassen in der objektorientierten Programmierung (OOP) auftreten kann. Es tritt auf, wenn eine Klasse von zwei übergeordneten Klassen erbt, die eine gemeinsame Methode mit derselben Signatur haben. Wenn nun eine Unterklasse diese Methode aufruft, kann sie nicht zwischen den beiden Implementierungen wählen und es tritt ein Kompilierungsfehler auf.
Das ist in Java so nicht möglich, da es dort keine Mehrfachvererbung gibt.
Ausgenommen ist die Vererbung über mehrere Interfaces. Der Compiler kennt hier aber die konkrete Methode die er aufrufen soll, da ein Interface abstract ist. Das bedeutet eine Klasse Test die 2 verschiedene Interfaces implementiert die unter anderem gleiche Methoden vorschreiben macht genau eine konkrete Methode mit dieser Signatur. Damit ist die Vorschrift für beide Interface erfüllt und trotzdem eindeutig.


Java:
public interface InterfaceA {
    public void methodeA();
    public void methodeC();
}
Java:
public interface InterfaceB {
    public void methodeB();
    public void methodeC() ;
}
Java:
public class Test implements InterfaceA, InterfaceB {
    @Override
    public void methodeA() {
        System.out.println("A");
    }

    @Override
    public void methodeB() {
        System.out.println("B");
    }

    @Override
    public void methodeC() {
        System.out.println("Ich mache das was mir die Klasse Test sagt!");
    }

}

In Java kann das Diamanten-Problem durch Verwendung des Schlüsselwortes "default" und "super" in der Interfaces-Linie gelöst werden, wobei "default" als eine Art "Fallback" Methode fungiert, die aufgerufen wird, wenn es mehrere mögliche Implementierungen gibt und "super" die Methode der übergeordneten Klasse aufruft.

Wenn ich mich richtig erinnere wurde das Schlüsselwort "default" bei Java 8 mit den Lambdafunktioen eingeführt. Der Grund war unter anderem, dass die Collection API mit Methoden erweitert wurden die Lambadas als Argumente verstehen.
Um zu verhindern, dass alte Programme angepasst werden müssen wurde default eingeführt.
Damit wurden nicht abstracte Methoden für das Interface zugelassen.
Also ist die default Methode eigentlich eine Ursache für das Diamantproblem.
Oder siehst Du das anders ?
 

KonradN

Super-Moderator
Mitarbeiter
A Auf Wikipedia kann jeder alles schreiben.

Daher gibt es eine History, damit klar ist, wer was wie schreibt. Man kann also sehr gut auch auf Wikipedia erkennen, was es an unterschiedlichen Sichtweisen gibt. Das ist aber hier schlicht nicht relevant aus meiner Sicht. Denn es geht einfach um ein paar Dinge:

a) Definition von Begriffen
Das Problem ist einfach, dass man in der Kommunikation eine Definition von Begriffen braucht. Es muss klar sein, über was man spricht. Wenn dies nicht klar ist, redet man einfach aneinander vorbei. Der Hinweis auf Wikipedia ist somit ein Verweis auf eine Begriffsdefinition. Es steht jedem frei, da eine andere Definition zu bringen. Dann kann man darauf auch gerne basieren.

b) Wikipedia macht Quellen offen. Da sieht man, wann wer was geschrieben hat. Und es gibt viele Aktive, die Änderungen prüfen. Damit ist die Datenqualität nicht schlecht oder zumindest gut abschätzbar.
Im Gegensatz dazu wurde ChatGPT gebracht. Also eine KI, die keine Quellen bringt und die aus unbekannten Quellen gefüllt wurde zu denen prinzipiell auch jeder beitragen konnte durch Veröffentlichungen und so. Des Weiteren ist klar, dass die KI im Falle einer schlechten Frage oder bei nicht guter Datenlage schlicht falsche Antworten gibt. ChatGPT Antworten sind damit aus meiner keine Basis für eine Kommunikation. Man kann drüber reden, aber Argumentationen darauf aufzubauen ist nicht wirklich möglich.

c) Hier ist es schlicht irrelevant. In dem Fall hier hat ChatGPT nichts anderes gesagt als auch schon durch Wikipedia beschrieben wurde. Das Problem ist ja eigentlich etwas wie:

  • Es kommt jemand, der jetzt in Physik Mechanik hat und fragt: Was ist Mechanik?
  • Antwort: Das kann zu schweren Verkehrsunfällen führen

Ja klar. Ohne Mechanik wären schwere Verkehrsunfälle nicht möglich: Ohne Mechanik würde es keine Autos, Züge, Flugzeuge, ... geben. Aber hilft das dieser Person weiter? (Wobei nicht direkt gefragt wurde, aber darauf lief es in #5 hinaus)

Aber wie dem auch sei - das werde ich nicht weiter vertiefen, da dies hier Off Topic ist. Ich denke, dem TE wurde weiter geholfen.
 
Ähnliche Java Themen

Ähnliche Java Themen

Neue Themen


Oben