Methodenaufrufe abstrakte Klassen, Interfaces

JWZ

Mitglied
Hallo,

ich lerne gerade Java und habe ein Verständnisproblem bei einer Aufgabe. Es soll entschieden werden, ob bestimmte Methodenaufrufe vom Compiler zugelassen werden und wenn ja, welche Ausgabe es gibt. Ich bin durch die Forensuche leider nicht schlauer geworden, deshalb hoffe ich, dass mir jemand helfen kann.

Es sind folgende Klassen gegeben:

Interface Inter{
public m(Hund h, Object o)
}

abstract class Super implements Inter{
public m(Lebewesen l1, Lebewesen l2)
}

class Sub extends Super{
public m(Hund h, Object o){
System.out.println("m1");
}
public m(Lebewesen l1, Lebewesen l2){
System.out.println("m2");
}
public m(Hund h, Tier t){
System.out.println("m3");
}
}

Außerdem:

class Lebewesen{}
class Tier extends Lebewesen{}
class Hund extends Tier{}

In der main-Methode werden jetzt folgende Objekte erzeugt:

Inter s1=new Sub();
Super s2=new Sub();
Hund h = new Hund();
Tier t = h;
Lebewesen l1=new Tier();
Lebewesen l2=new Tier();

Frage:
1. beim Aufruf s1.m(h,t); wird "m1" ausgegeben. Der Deklarationstyp von s1 ist Inter, deshalb kommen nur Methoden m() in Frage, die in Inter deklariert sind. Es wird jetzt die speziellste der passenden Methoden bzgl. der Parameter gesucht, hier gibt es nur eine und die Signatur passt. Diese wird dann aufgrund des Typen der Instanz, die s1 referenziert dynamisch an die überschriebene Methode in Sub gebunden. So habe ich das zumindest verstanden.

2. wieso erzeugt der Aufruf s2.m(h,t) dann einen Compilerfehler? Die Methode in Super passt auf den Aufruf, denn die Deklarationstypen der Übergebenen Parameter sind Subtypen der Parameter in der Methodensignatur. Diese müsste doch dann genau wie in 1. dynamisch in Sub gebunden werden (also "m2" ausgeben). Falls die Methode in Inter auch in Super als abstrakte Methode existiert (dabei bin ich mir nicht sicher), dann müsste diese gewählt werden, denn sie ist spezieller oder täusche ich mich?

Ein weiterer Aufruf wäre z.B. s2.m(l1,l2), das funktioniert, weil es eine Methode m() in Super gibt, auf dessen Signatur der Aufruf passt. Die Methode wird dann aufgrund des Referenztypen in Sub dynamisch gebunden und es wird "m2" ausgegeben.

Mein Vorgehen ist also folgendes: Ich schaue mir als erstes den Deklartionstypen der Variablen an, der die Methode aufruft (z.B. Inter). Dann suche ich mir alle Methoden in dieser Klasse (geerbt und deklariert) raus, bei denen die Signatur passt. Bei den übergebenen Parametern ist hier nur der Deklarationstyp von Bedeutung, z.B. bei s1.m(h,t) Hund H und Tier t. Sollten jetzt noch mehr als eine Methode übrig sein, wird die speziellste (most-specific-algorithm) gewählt. Anschließend schaue ich mir den dynamischen Typen von s1 an, hier wäre es Sub, und finde damit die auszuführende Methode in Sub.

Wo mache ich den Fehler? Oder habe ich irgendwas total falsch verstanden?
 
Also das compiliert nicht, da ein ; fehlt:
Java:
Interface Inter{
public m(Hund h, Object o)
}
Somit compiliert alles andere auch nicht. Der Compiler wird also nichts weiter zulassen.
 
Also das compiliert nicht, da ein ; fehlt:
Java:
Interface Inter{
public m(Hund h, Object o)
}
Somit compiliert alles andere auch nicht. Der Compiler wird also nichts weiter zulassen.
oh ja, sorry, da hast du recht. Ich hab das einfach schnell hin geschrieben. Es geht mir nicht um syntaktische Fehler, sondern darum, dass in der Lösung der Aufgabe steht, dass der Aufruf von 2. einen Compilerfehler erzeugt und ich nicht verstehe wieso. Nehmen wir also an, dass keine Semikolon, Klammern etc. vergessen wurden.
 
oh ja, sorry, da hast du recht. Ich hab das einfach schnell hin geschrieben. Es geht mir nicht um syntaktische Fehler, sondern darum, dass in der Lösung der Aufgabe steht, dass der Aufruf von 2. einen Compilerfehler erzeugt und ich nicht verstehe wieso. Nehmen wir also an, dass keine Semikolon, Klammern etc. vergessen wurden.
 
Super hat "public m(Lebewesen l1, Lebewesen l2) "
und nicht
s2.m(h,t) also "public m(Hund h, Object o)" oder "public m(Hund h, Tier t)"

das hat die Klasse SUB aber nicht Super.

Du hast aber den Typ Super (Super s2=new Sub()😉



 
Super hat "public m(Lebewesen l1, Lebewesen l2) "
und nicht
s2.m(h,t) also public m(Hund h, Object o)
Doch, natürlich hat Super auch m(Hund, Object), denn Super implementiert Inter. Also mit paar Fixes sehen das Interface und die Klasse so aus:

Java:
Interface Inter{
    void m(Hund h, Object o);
}

abstract class Super implements Inter{
    public abstract void m(Lebewesen l1, Lebewesen l2);
}

Somit hat eine Instanz in einer Variable Super die Methoden:
void m(Hund h, Object o)
und
void m(Lebewesen l1, Lebewesen l2)

Mein Tipp: Schau es Dir doch einmal selbst in der IDE an ...
 
Doch, natürlich hat Super auch m(Hund, Object), denn Super implementiert Inter. Also mit paar Fixes sehen das Interface und die Klasse so aus:

Java:
Interface Inter{
    void m(Hund h, Object o);
}

abstract class Super implements Inter{
    public abstract void m(Lebewesen l1, Lebewesen l2);
}

Somit hat eine Instanz in einer Variable Super die Methoden:
void m(Hund h, Object o)
und
void m(Lebewesen l1, Lebewesen l2)

Mein Tipp: Schau es Dir doch einmal selbst in der IDE an ...
ja das stimmt
aber er will doch public m(Hund h, Tier t) und das ist erst in Sub.


edit: Und die Methode mit Hund, Objekt wird beim Implantieren auch nicht erstellt.
 
ja das stimmt
aber er will doch public m(Hund h, Tier t) und das ist erst in Sub.
Aber Hund -> Tier -> Lebewesen -> Object
Damit passen die Argumente Hund, Tier sowohl auf Hund, Object (Tier ist ein Object) als auch auf Lebewesen, Lebewesen, denn sowohl Hund als auch Tier sind Lebewesen.

Das Problem ist daher nicht, dass es keine Methode gibt, die verwendet werden könnte sondern, dass es zwei Methoden gibt, die in Frage kommen können. Und das erklärt auch den Punkt 2 vom TE.
 
Aber Hund -> Tier -> Lebewesen -> Object
Damit passen die Argumente Hund, Tier sowohl auf Hund, Object (Tier ist ein Object) als auch auf Lebewesen, Lebewesen, denn sowohl Hund als auch Tier sind Lebewesen.

Das Problem ist daher nicht, dass es keine Methode gibt, die verwendet werden könnte sondern, dass es zwei Methoden gibt, die in Frage kommen können. Und das erklärt auch den Punkt 2 vom TE.
ok
 
Aber Hund -> Tier -> Lebewesen -> Object
Damit passen die Argumente Hund, Tier sowohl auf Hund, Object (Tier ist ein Object) als auch auf Lebewesen, Lebewesen, denn sowohl Hund als auch Tier sind Lebewesen.

Das Problem ist daher nicht, dass es keine Methode gibt, die verwendet werden könnte sondern, dass es zwei Methoden gibt, die in Frage kommen können. Und das erklärt auch den Punkt 2 vom TE.
Beim Aufruf s2.m(h,t) kommen also die Methoden m(Lebewesen l1, Lebewesen l2) aus Super und m(Hund h, Object o) aus Inter in Frage. Beim ersten Parameter ist m(Hund h, Object o) spezieller, denn h ist ein Hund. Beim zweiten Parameter sind bei beiden Methoden die Parameter Supertypen vom übergebenen Parameter Tier, es gibt also keinen spezielleren. Damit müsste doch die Methode m(Hund h, Object o) gewählt werden weil sie insgesamt spezieller ist oder nicht?

Einen Compilerfehler würde ich hier bei diesem Methodenaufruf verstehen, wenn z.B. die erste Methode die Signatur m(Lebewesen, Tier) anstatt m(Lebewesen, Lebewesen) hätte.
 
Ich denke, der wichtige Punkt aus der JLS aus 15.12.2.5 ist:
  • m2 is not generic, and m1 and m2 are applicable by strict or loose invocation, and where m1 has formal parameter types S1, ..., Sn and m2 has formal parameter types T1, ..., Tn, the type Si is more specific than Ti for argument ei for all i (1 ≤ in, n = k).

Auf Deutsch ist die Regel zu der spezifischsten Methode dann (jetzt einfach von ChatGPT erstellt):
Die spezifischste Methode gewinnt: Wenn mehrere Methoden aufgrund der oben genannten Konversionen passen, wählt Java die "spezifischste" Methode aus, d.h. die Methode, deren Parametertypen am besten zu den Argumenttypen passen. Eine Methode M1 gilt als spezifischer als eine Methode M2, wenn jeder Parameter von M1 vom gleichen oder einem abgeleiteten Typ jedes entsprechenden Parameters in M2 ist.

Also haben wir zwei Methoden mit den Parameter-Typen:
Hund / Object und Lebewesen / Lebewesen

Also vergleichen wir einmal:
Hund ist spezifischer als Lebewesen (Hund erbt von Tier erbt von Lebewesen)
Aber Object ist nicht spezifischer als Lebewesen. (Lebewesen erbt von Object)

Interessant wäre auch mal welchen Fehler du genau bekommst.
Wie schon gesagt: Spiel es doch einfach durch! Das ist der "Ambiguous method call" Fehler, der besagt, dass mehrere Methoden passen würden.
 
Ich denke, der wichtige Punkt aus der JLS aus 15.12.2.5 ist:


Auf Deutsch ist die Regel zu der spezifischsten Methode dann (jetzt einfach von ChatGPT erstellt):


Also haben wir zwei Methoden mit den Parameter-Typen:
Hund / Object und Lebewesen / Lebewesen

Also vergleichen wir einmal:
Hund ist spezifischer als Lebewesen (Hund erbt von Tier erbt von Lebewesen)
Aber Object ist nicht spezifischer als Lebewesen. (Lebewesen erbt von Object)


Wie schon gesagt: Spiel es doch einfach durch! Das ist der "Ambiguous method call" Fehler, der besagt, dass mehrere Methoden passen würden.
Ich denke jetzt geht mir ein Licht auf! Ich dachte eine Methode m1 ist spezifischer als m2, wenn m1 mehr Parameter hat, dessen Typen genau mit den übergebenen Typen überein stimmen. Deshalb dachte ich, dass m(Hund,Object) spezifischer ist, da hier ja der erste Parameter genau übereinstimmt und bei m(Lebewesen,Lebewesen) stimmen beide nicht genau überein.
Mit der Definition macht das jetzt natürlich Sinn, denn Hund ist spezifischer als Lebewesen im ersten, aber Lebewesen ist spezifischer als Object im zweiten Parameter, so wie du erklärt hast!

Vielen Dank, damit wurde mir sehr für das Verständnis geholfen!
 
spezifischer...Verständnis
Generell geht es in der Programmierung darum, wie sich der Compiler ( Interpreter/Transpiler/...) verhält (z.B. via JLS definiert), wenn er auf ein Schlüsselwort trifft (oder i.V. mit anderen Schlüsselwörtern (nicht) trifft). Auch in der analogen Welt lässt z.B. eine Kaufanweisung für "eine Packung Salz von Hersteller X mit 500 Gramm" weniger Interpretation zu als "eine Packung Gewürz" (vorausgesetzt, es gibt etwas zu kaufen)
 

Zurück
Oben