Mehrfachvererbung (Deadly diamond of death)

paulyST

Cadet 4th Year
Registriert
Nov. 2013
Beiträge
105
Hallo zusammen,

ich lese mich derzeit verstärkt in Java ein und bin nun auf das Diamant-Problem gestoßen. In Java wird das mittels Interfaces umgangen. Somit MUSS in der Unterklasse genau spezifiziert werden, was eine Methode kann/welchen Wert eine Variable hat. WAS ABER, wenn folgendes Szenario auftritt:

1. Unterklasse erbt eine Methode, z.b. testen() und eine Variable "testErgebnis")
2. Unterklasse benutzt ein Interface mit der methode testen() und einer Variable names "testErgebnis"?

Habe ich dann nicht genau das Problem, dass durch Interfaces gelöst werden soll? Nämlich das Diamant-Problem, bzw. keine klare Regel, auf welche Variable/Methode zugegriffen wird (die von der Superklasse, oder die, vom Interface?) Oder wird in so einem Fall ausnahmslos die Member der Superklasse benutzt (da diese evtl. schon implementiert sind und die vom Interface mit Sicherheit abstrakt sind)?

Ich hoffe, ihr versteht was ich meine ;)
 
In Java können Unterklassen Methoden überschreiben - diese werden mit @override gekennzeichnet

http://de.wikibooks.org/wiki/Java_Standard:_Vererbung

Wenn mein Objekt zur Subklasse gehört, dann wird auch die Methode der Subklasse aufgerufen.
Variablen würde ich nicht public machen sondern private und mit get und set drauf zugreifen.
 
Ein Interface beschreibt immer nur Methoden der implementierenden Klasse und kann keine Variablen oder Methoden enthalten. Daher wird immer die vererbte Methode benutzt, wenn diese nicht überschrieben wird. Das Interface ändert daran nichts.
 
phm666 schrieb:
Ein Interface beschreibt immer nur Methoden der implementierenden Klasse und kann keine Variablen oder Methoden enthalten.

Seit Java 8 können sehr wohl Methoden definiert werden. Und Variablen gehen schon immer, nur sind diese halt immer public final (also effektiv Konstanten).
 
Ich formuliere mein Problem mal etwas anders, vielleicht verstehe ich es dann:
Wenn meine Unterklasse eine implementierte Methode names testen() erbt, gleichzeitig aber auch ein interface besitzt, dass zufälligerweise auch eine Methode namens testen() besitzt. Nun müsste ich doch die Methode testen() des Interfaces implementieren. Das Problem ist aber, dass diese Methode bereits existiert und zwar aufgrund der Vererbung durch eine andere Klasse.
Somit ist es nicht eindeutig, welche testen()-Methode bei Aufruf verwendet werden soll. Habe ich einen Denkfehler, oder bin ich mit meiner Annahme völlig falsch?
 
Wenn die visibility von testen() der Superklasse public ist, ist das kein Problem. Dann ist das Interface zufrieden damit, dass diese Methode aufrufbar ist. Wenn allerdings z.B. default (aka package-private) visibility in der Superklasse anliegt, dann gibt es Ärger
The inherited method SomeAbstractClass.test() cannot hide the public abstract method in SomeInterface
 
Das heisst also, dass die Methode der Superklasse Vorrang hat und ich die Methode des Interfaces nicht mehr implementieren muss/kann. Klingt für mich irgendwie inkonsequent, da ja Interfaces dafür da sind, um ein "gemeinsames Protokoll" zu definieren. Nun kann dieses Protokoll aber nicht mehr eingehalten werden, da es bereits eine Methode mit gleichem Namen gibt?
 
Das interface will erfüllt werden. Eine Klasse, die das interface implementiert, muss also eine solche Methode zur Verfügung stellen. Das tut deine konkrete Implementierung ja, weil deren Superklasse schon eine Implementierung mitbringt. Daher musst du keine eigene Implementierung erstellen, kannst aber.
 
In Ordnung, WAS aber, wenn ich die Methode der Superklasse behalten möchte, gleichzeitig aber auch das Interface erfüllen will. Eine Methode hat aber sowohl in Superklasse, als auch im Interface den gleichen Namen mit gleichen Argumenten/Rückgabetypen, erfüllt aber im Body einen ganz andere Zweck. Kann man sagen, dass in diesem Fall schlichtweg der Fehler beim Programmierer lag, da dieser die Methoden gleich benannt hat, obwohl diese unterschiedlich funktionieren sollen?
 
Machen wir es mal konkret:

Code:
public class SomeClass extends SomeAbstractClass implements SomeInterface {

}
Code:
public abstract class SomeAbstractClass {

    public void test(final int myNumber) {
        System.out.println("SomeAbstractClass");
    }
}
Code:
public interface SomeInterface {

    void test(int myNumber);
}
Code:
public class Sandbox {

    public static void main(final String[] args) {
        final SomeClass some = new SomeClass();
        some.test(5);
    }
}
Das kompiliert, hat eine Implementierung in der Superklasse und erfüllt das Interface. Das ist doch was du willst oder?
 
Tumbleweed schrieb:
Das kompiliert, hat eine Implementierung in der Superklasse und erfüllt das Interface. Das ist doch was du willst oder?

Eben nicht. In deinem Beispiel wird test() wie in der Superklasse beschrieben ausgeführt. Mit Hilfe des Interface ist aber vorgesehen, dass die Methode sagen wir nicht wie in deiner Superklasse etwas ausgibt, sondern eine Methode besitzt, die einen Test auf irgendeinem anderen Objekt ausführt. Nun möchte ich/benötige ich aber auch gleichzeitig die Ausgabe (wie in deiner Superklasse). Wie bringe ich es nun unter einem Hut, dass ich zum einen die Absicht des Interfaces erfülle (nämlich eine Methode besitze, die Objekte testet), ohne dadurch die vererbte Methode der Superklasse zu verlieren (die etwas ausgeben kann). Beide besitzen doch den gleichen Namen und ich muss mich nun entweder für das Interface, oder die Superklasse entscheiden. Somit bin ich wieder beim Diamant-Problem und bei meinem ersten Post ;)
 
paulyST schrieb:
Beide besitzen doch den gleichen Namen und ich muss mich nun entweder für das Interface, oder die Superklasse entscheiden. Somit bin ich wieder beim Diamant-Problem und bei meinem ersten Post ;)
Das ist nicht das Diamant-Problem, weil du eigentlich keine zwei Methoden hast.
Du hast einerseits die Methode test() der Superklasse und andererseits die Anforderung des Interfaces, dass es eine Methode test() in implementierenden Klassen geben muss. In deinem Fall ist diese geforderte Implementierung eben die Methode test() der Superklasse, weil sie die im Interface geforderte Signatur (also Name + Parameterliste) hat.
Im Endeffekt beschreibst du eine Signaturkollision, die man am einfachsten durch Umbenennen löst.

paulyST schrieb:
Nun möchte ich/benötige ich aber auch gleichzeitig die Ausgabe (wie in deiner Superklasse). Wie bringe ich es nun unter einem Hut, dass ich zum einen die Absicht des Interfaces erfülle (nämlich eine Methode besitze, die Objekte testet), ohne dadurch die vererbte Methode der Superklasse zu verlieren (die etwas ausgeben kann).
Was du in deiner Implementierung machen kannst ist das:
Code:
public void testen() {
    super.testen(); // testen() der Basisklasse aufrufen.

    // Neue Logik für das Interface
}
Dadurch wird zunächst die Methode der Basisklasse ausgeführt und dann deine Logik.
Wenn du im Aufruf entscheiden willst, welche Logik ausgeführt wird geht das schlicht und einfach nicht.

In der Praxis ist das weniger ein Problem (zumindest in meiner Erfahrung), weil sich meist die Parameterliste unterscheidet oder die Basisklasse eh vom gleichen Interface erbt.
 
Zurück
Oben