Unterschied zwischen List und LinkedList implementierung?

Lyreex

Aktives Mitglied
Hallo,

ich hätte da mal eine Verständnis frage zur Implementierung von Listen.

[CODE lang="java" title="Implementierung gegen das Interface Liste"]List<String> list = new LinkedList<>();
list.add("def");
list.addFirst("abc") //Error -> addFirst gibts nicht[/CODE]

[CODE lang="java" title="Implementierung gegen konkrete List Klasse"]LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("def");
linkedList.addFirst("abc"); //Kein Error, addFirst existiert[/CODE]

In meinem ersten Code Beispiel hab ich gegen das Interface implementiert aber dann nicht die eigentlichen Funktionen von
Java:
LinkedList<>()
während im zweiten Beispiel die Funktionen da sind. Da wird auch direkt gegen
Java:
LinkedList<>()
implementiert.

Aber warum geht es bei dem ersten Beispiel nicht? Ich gebe doch beim initialisieren den konkreten Typ an.
Ich dachte man sollte am besten immer gegen das Interface implementieren?
 
List ist ein Interface, das bestimmte Methoden vorgibt. Wenn Du gegen dieses Interface programmieren willst, dann musst Du Dich auf dieses Interface beschränken.
Eine Klasse, die das Interface implementiert kann natürlich noch weitere Methoden haben.

Das Beispiel bei Dir ist genau das: List kennt keine Methode addFirst.

Du müsstest also statt dessen z.B. add(int, E) nutzen.
 
Okay, aber wenn ich die Methoden von LinkedList verwenden will, dann darf ich nicht gegen das List Interface implementieren, sondern muss direkt gegen LinkedList implementieren oder?
 
Bei einer Vererbung darf eine klasse nur um Funktionen erweitert werden ansonsten wäre die Vererbung relativ sinnlos 😀


wenn du die LinkedList erweitern willst um etwas solltest du diese extenden dann hast du auch die ganzen methoden der LinkedList...


willst du deine eigene Liste bauen sollte man das List interface benutzen
 
Alles klar.
Aber macht es dann Sinn trotzdem immer gegen das List Interface zu implementieren oder sollte man direkt gegen die entsprechende Klasse implementieren? Also was wäre so Best Practice?

Mein Gedanke: Wenn ich gegen das List Interface implementiere, dann könnte ich an einer anderen Stelle meine Meinung ändern und einfach ein Objekt von z.B. der LinkedList erstellen und die Elemente von List reinpacken. Dann hätte ich meine LinkedListe mit all ihren Funktionen.
Oder gäbe es dafür einen besseren Ansatz?
 
Okay, aber wenn ich die Methoden von LinkedList verwenden will, dann darf ich nicht gegen das List Interface implementieren, sondern muss direkt gegen LinkedList implementieren oder?
Wenn Du Methoden verwenden willst, die nicht zu List gehören, dann ist eine Entwicklung gegen List nicht sinnvoll. Du nutzt nur dann ein Interface, wenn es die Möglichkeiten bietet, die du brauchst. Wenn es Deine Anforderungen nicht erfüllt, dann taugt es für diese spezielle Nutzung, die Du vorhast, nichts.

Daher ist immer die Frage, was Du genau brauchst / willst. Und wenn statt .addFirst("abc"); auch ein .add(0, "abc"); Deine Anforderung erfüllt, dann würde List auch wieder gehen.
 
Alles klar.
Aber macht es dann Sinn trotzdem immer gegen das List Interface zu implementieren oder sollte man direkt gegen die entsprechende Klasse implementieren? Also was wäre so Best Practice?

Mein Gedanke: Wenn ich gegen das List Interface implementiere, dann könnte ich an einer anderen Stelle meine Meinung ändern und einfach ein Objekt von z.B. der LinkedList erstellen und die Elemente von List reinpacken. Dann hätte ich meine LinkedListe mit all ihren Funktionen.
Oder gäbe es dafür einen besseren Ansatz?
Also wenn Du spezielle Dinge brauchst, die List nicht bietet, aber Du nur eine List hast, dann musst Du da mit irgendwas aktiv werden. Da kann das Beschriebene möglich sein. Das bringt Dir aber nicht unbedingt das, was Du willst, denn dann hast Du eine neue Instanz und damit veränderst Du ja nicht die List, die übergeben wurde.

Also ausführlich:
Du übergibst eine List an eine Methode: 1, 2, 3
Da Du nun addFirst machen willst, erzeugst Du eine LinkedList mit den Elementen der List und rufst addFirst(0) auf.
Dann ist die übergebene List immer noch 1, 2, 3 und nur in der Linked List, die Du erzeugt hast, ist jetzt 0, 1, 2, 3 enthalten.

Was wie sinn macht, muss man immer im Detail sehen. Die Java Collections sind aber schon recht gut durchdacht, so dass die meisten Anwendungsfälle durchaus schon bedacht wurden.

Generell solltest Du Daten so modellieren, dass Du alle notwendigen Operationen durchführen kannst. Wie Du die Daten intern hältst ist dabei egal - du solltest es nur sauber kapseln, so dass dies eben nicht nach außen geht. Dann kannst Du es auch jederzeit so ändern, wie Du es brauchst.
 
Okay, aber wenn ich die Methoden von LinkedList verwenden will, dann darf ich nicht gegen das List Interface implementieren, sondern muss direkt gegen LinkedList implementieren oder?
Schon der Titel deiner Frage ist verwirrend: Eine List hat keine (direkte) Implemnetierung, eine LinkedList schon.
Du solltest begrifflich zwischen der Implementierung einer Klasse und der Benutzung einer Klasse (eines Interfaces) unterscheiden.
 
dann komplett selber schreiben und alles neu erfinden ..nix extenden..


bitte wenigstens dann dazu schreibne wie es "richtig" gemacht wird
Nein, neu schreiben muss man auch nichts – man kann LinkedList durchaus intern nutzen.
Vererbung sollte man aber nur nutzen, wenn Klassen dafür explizit vorgesehen sind – und das ist nahezu keine.

Einfaches Beispiel mit Listen: die Liste soll zählen, wie viele Elemente insgesamt hinzugefügt wurden.
Welche Methoden müsstest du jetzt beim extenden von einer beliebigen Listen-Implementation überschrieben und um das zählen erweitern, damit es korrekt funktioniert?
add, addAll, den Konstruktor der eine Collection übergeben bekommt?
 
Nein, neu schreiben muss man auch nichts – man kann LinkedList durchaus intern nutzen.
Vererbung sollte man aber nur nutzen, wenn Klassen dafür explizit vorgesehen sind – und das ist nahezu keine.

Einfaches Beispiel mit Listen: die Liste soll zählen, wie viele Elemente insgesamt hinzugefügt wurden.
Welche Methoden müsstest du jetzt beim extenden von einer beliebigen Listen-Implementation überschrieben und um das zählen erweitern, damit es korrekt funktioniert?
add, addAll, den Konstruktor der eine Collection übergeben bekommt?
Man muss ja auch nicht die bereits "fertigen" Methoden überschreiben...


So wie ich Vererbung gelernt habe ist der "absolut banalste Grundsatz der Vererbung" man muss die Klasse erweitern und nicht einschränken..
wenn man jetzt extended und nur 1 Methode zusätzlich hinzufügt sollte es in Ordnung gehen, da man die Klasse von vorher hat PLUS 1 Methode mehr

Das geht jetzt nicht speziell um Listen sondern allgemein um Vererbung
 
Was leider beim Lernen von Vererbung meist untergeht ist die erste und wichtigste Regel zu Vererbung: Vererbung ist der falsche Weg. (Ausnahmen bestätigen die Regel)

Vor allem wenn du eine Klasse nur um Funktionen erweitern willst und keine bestehende Funktionalität ändern willst, ist Vererbung der falsche Weg. Grad dann ginge auch einfache Delegation.
 
Ein Stichwort, das da evtl. erwähnt werden sollte: "composition over inheritance".

Im Buch Effective Java ist es Topic 16.

Das einfach mal um Stoff für Google oder für ein Buch zu nennen.
 
Es gibt den schönen Spruch "Composition over Inheritance". In den seltensten Fällen will man wirklich eine Standardklasse wie LinkedList ableiten (bzw. bei LinkedList würde ich sogar sagen, es gibt gar keinen Fall). Sondern man möchte Funktionalität von LinkedList nutzen und ergänzen. Und das ist eher Komposition-

Ein Ableitung - auch wenn das Schlüsselwort extends heißt - ist immer eine Spezialisierung (Das wird z.B. bei CDI mit der Annotation Specialize sehr deutlich).

Eine Ableitung einer Klasse X muss die Funktionalität, die von der Klasse X spezifiziert wird beibehalten. Das heißt, den Rahmen den die Klasse X vorgibt, der darf nicht verlassen werden. Nehmen wir mal eins der klassischen Beispiele:

Java:
public class Circle extends Shape

Jeder Kreis ist ein Shape, das heißt die Klasse Circle erweitert zwar die Klasse Shape, aber sie muss alles was die Klasse Shape kann auch können. Sie kann zwar noch die Dinge, die für Kreise speziell sind, aber eben auch alles aus Shape. Und während jeder Circle ein Shape ist, ist nicht jedes Shape ein Circle. Ergo ist Circle die speziellere Klasse.

Und in der Regel sind die Klassen aus der Standardbibliothek (sofern sie nicht zum ableiten Vorgesehen sind, wie z.B. diverse Abstrakte Listener Klassen) bereits so, dass eine weitere Spezialisierung keinen Sinn ergibt.
 

Neue Themen


Zurück
Oben