Ich glaub das hat irgendwas mit Polymorphism zu tun. Zumindest die verschiedenen Deklarationsvarianten. Bei dem, wenn man beim Konstruktoraufruf den Typ weglässt, denk ich nimmt er irgendeinstandartwert oder so...
List ist ein Interface, welches von ArrayList implementiert wird.
Daher kann man auch
List<T> list=new ArrayList<T>()
schreiben.
Die Typen in den spitzen Klammern sind die Generics, die mit Java 1.5 eingeführt wurden.
Man spezifiziert damit den Inhalt der Liste.
List<String> list = new ArrayList<String>()
erlaubt nur Strings in der Liste. Vor Java 1.5 waren nur Objects in der Liste, daher musste bei Strings immer mit
String string = (String) list.get(0)
das Object in einen String gecastet werden. Mit den Generics ist das nicht mehr notwendig. Aus Rückwärtskompatibilitätsgründen (geiles Wort) geht das immer noch wie vor Java 1.5. Daher gibts keinen Compilerfehler, wenn der Typ nicht angegeben wird. Sollte man aber eigentlich nicht mehr machen.
Soll das heissen, dass es keinen Unterschied macht, ob nun List oder ArrayList?
Die Typen in den spitzen Klammern sind die Generics, die mit Java 1.5 eingeführt wurden.
Man spezifiziert damit den Inhalt der Liste.
List<String> list = new ArrayList<String>()
Das leuchtet mir ja auch ein. Für mein Verständnis müsste aber zwingend rechts der gleiche Objekttyp wie links stehen... was zählt denn nun, wenns unterschiedlich ist, links oder rechts?
erlaubt nur Strings in der Liste. Vor Java 1.5 waren nur Objects in der Liste, daher musste bei Strings immer mit
String string = (String) list.get(0)
das Object in einen String gecastet werden. Mit den Generics ist das nicht mehr notwendig. Aus Rückwärtskompatibilitätsgründen (geiles Wort) geht das immer noch wie vor Java 1.5. Daher gibts keinen Compilerfehler, wenn der Typ nicht angegeben wird. Sollte man aber eigentlich nicht mehr machen.
Der Grund, warum man hier besser das Interface verwendet, ist ganz einfach: List ist "allgemeiner".
Man kann dann ohne Probleme durch die Änderung einer einzigen Zeile eine andere Listen-Implementierung nehmen. Wenn man z.B. anfängt, im Programm mit mehreren Threads zu arbeiten, und Probleme beim gleichzeitigen Zugriff auf die Liste bekommt, braucht man nur die Zuweisung zu ändern:
Java:
List<String> list =newCopyOnWriteArrayList<String>();
Der Rest des Codes bleibt unverändert, auch wenn "unter der Haube" jetzt eine andere, thread-sichere Liste werkelt.
Bei den Listen geht es hauptsächlich um die Funktionalität, und nicht um die Implementierung, genau wie es dir im Restaurant bei den Gabeln auch mehr um gerade Zinken und Sauberkeit geht, als ob die Gabel von WMF oder dem lokalen Hufschmied hergestellt worden ist. Und genau wie du im Restaurant zum Kellner sagen würdest "Bringen Sie mir bitte eine saubere Gabel!" und nicht "Bringen Sie mir bitte eine saubere Gabel von WMF", sagt man in Java halt "Gib mir eine Liste", wenn es nicht so sehr auf den genauen Typ ankommt (was bei Listen eher selten der Fall ist).
Der Grund, warum man hier besser das Interface verwendet, ist ganz einfach: List ist "allgemeiner".
Man kann dann ohne Probleme durch die Änderung einer einzigen Zeile eine andere Listen-Implementierung nehmen. Wenn man z.B. anfängt, im Programm mit mehreren Threads zu arbeiten, und Probleme beim gleichzeitigen Zugriff auf die Liste bekommt, braucht man nur die Zuweisung zu ändern:
Java:
List<String> list =newCopyOnWriteArrayList<String>();
Der Rest des Codes bleibt unverändert, auch wenn "unter der Haube" jetzt eine andere, thread-sichere Liste werkelt.
Also ganz doof gefragt: Es ist doch auch nur die eine gleiche Zeile geändert, wenn ich links das auch anpasse, oder muss ich dann im nachfolgenden Code Anpassungen vornehmen?
Java:
CopyOnWriteArrayList<String> list =newCopyOnWriteArrayList<String>();
Bei den Listen geht es hauptsächlich um die Funktionalität, und nicht um die Implementierung, genau wie es dir im Restaurant bei den Gabeln auch mehr um gerade Zinken und Sauberkeit geht, als ob die Gabel von WMF oder dem lokalen Hufschmied hergestellt worden ist. Und genau wie du im Restaurant zum Kellner sagen würdest "Bringen Sie mir bitte eine saubere Gabel!" und nicht "Bringen Sie mir bitte eine saubere Gabel von WMF", sagt man in Java halt "Gib mir eine Liste", wenn es nicht so sehr auf den genauen Typ ankommt (was bei Listen eher selten der Fall ist).
Das klingt zwar einleuchtend, jedoch scheint genau hier mein Verständnisproblem zu liegen, denn so interpretiere ich es im Moment (noch):
Links (des Gleichheitszeichens) sag ich - um in Deinem Beispiel zu bleiben - "bring mir ne Gabel..." und rechts schiebe ich nach "...aber bitte von WMF".
Was ja zum (gleichen) Ergebnis führt, dass ich hinterher ne Gabel habe, die von WMF ist (und mit der ich nur <Strings> essen kann).
Mit der Gabel kannst du essen. Mehr nicht. An mehr "kommst du auch nicht ran", wenn du sagst "Gib mir ne Gabel".
g hat alle Eigenschaften der Gabel (klar, hat ja auch den Typ)
Die WMFGabel hat auch die Eigenschaften der Gabel, denn sie erbt ja von Gabel alles.
Die von WMF hat aber vielleicht noch weitere Eigenschaften, die ganz nützlich sind.
Dann musst du aber sagen:
WMFGabel g=new WMFGabel()
Dann hat g alle Eigenschaften der WMFGabel (auch klar, hat ja auch den Typ)
Links (des Gleichheitszeichens) sag ich - um in Deinem Beispiel zu bleiben - "bring mir ne Gabel..." und rechts schiebe ich nach "...aber bitte von WMF".
Was ja zum (gleichen) Ergebnis führt, dass ich hinterher ne Gabel habe, die von WMF ist (und mit der ich nur <Strings> essen kann).
Du wirst dein Objekt zu 99.9% irgendwo übergeben und verwenden müssen. Egal ob in einer "eigenen" Methode (falls Klassenattribut) oder nicht. Da kann man noch problemlos an [c]doSomething(List)[/c] den Typen [c]ArrayList[/c] übergeben. Spätestens wenn du aber von extern eine Liste erwartest, willst du nicht eine Implementierung wie [c]ArrayList[/c] vorgeben.
[c]List[/c] ist nur ein Interface... Jede Implementierung davon ([c]ArrayList[/c], [c]LinkedList[/c] etc.) hat ihre Vor- und Nachteile - ganz abhängig davon was man machen will
Also ganz doof gefragt: Es ist doch auch nur die eine gleiche Zeile geändert, wenn ich links das auch anpasse, oder muss ich dann im nachfolgenden Code Anpassungen vornehmen?
Mag sein, daß es hier nur eine Zeile betrifft, aber ohne den kompletten Kontext kann man das nicht sagen. Es kann auch sein, daß es in diesem Fall egal, eine Frage des Geschmacks oder der Gewohnheit ist; deutlicher wird es hingegen hier:
Möchte man nun, daß dem Konstruktor LinkedList übergeben werden kann – auch LinkedList implementiert die Schnittstelle List –, so müßte man aus ArrayList nun LinkedList machen. Wenn aber LinkedList übergeben wird, müßte auch die Variable strings zu LinkedList gemacht werden, und deswegen kann auch getStrings nur LinkedList zurückgeben.
Je allgemeiner man beim Programmieren bleibt, desto flexibler läßt sich ein Programm später ändern. So lange man nicht ganz spezielle Funktionen benötigt, die nur ArrayList oder LinkedList anbietet, sollte man daher so allgemein wie möglich bleiben.
Benutzt man statt ArrayList oder LinkedList gleich List, legt sich also nicht auf eine konkrete Implementierung fest, dann braucht man auch nichts ändern, sondern es kann ArrayList und LinkedList übergeben werden. Man nennt das gegen Schnittstellen programmieren.
Je größer Projekte werden und je länger ihre Lebenszyklus ist, desto wichtiger ist die Programmierung gegen Schnittstellen, sonst kann eine eigentlich kleine Änderungen so viele Folgeänderungen nach sich ziehen, daß viel Arbeit ensteht und viele Fehler enstehen können. Und wenn man eine Klasse programmiert, die einen Dienst anbietet, die etwas mit Listen macht, weiß man gar nicht, ob ein Nutzer dieser Klasse jetzt eigentlich ArrayList oder LinkedList verwendet, und es gibt keinen guten Grund, ihm unnötige Beschränkungen aufzulegen oder ihn zu zwingen, seinen Quelltext zu ändern.
Verwendet man eine Liste nur lokal, vielleicht nur als kleines Hilfkonstrukt, ist der größte Vorteil, lediglich List zu verwenden, wohl der, daß die Zeile kürzer wird.
Wer gibt wem was vor? Du meinst, wenn ich mir (von aussen) eine Liste an eine Methode übergeben lasse? Aber da kann ich doch sehr wohl Bedingungen stellen, oder etwa nicht? Immerhin entscheide ich mich ja für einen Typ, weil der mir gewisse (erwünschte) Vorteile bringt.
z.B. mir die WMFGabel noch die dringend benötigte Temperatur mit anzeigt. Wenn ich die nicht bräuchte, würde ich doch nicht eine WMF anfordern...
Klar, wenn du wirklich eine spezifische Eigenschaft (z.B. Methode) einer Implementation brauchst, dann kannst du natürlich auch diese erwarten wollen. Aber ganz grundsätzlich als Beispiel die [c]List[/c]: List (Java Platform SE 6) - fehlt dir hier irgendwas, was du normalerweise benötigst?
[...] Je allgemeiner man beim Programmieren bleibt, desto flexibler läßt sich ein Programm später ändern. So lange man nicht ganz spezielle Funktionen benötigt, die nur ArrayList oder LinkedList anbietet, sollte man daher so allgemein wie möglich bleiben.
Das leuchtet mir sehr wohl ein. Vielleicht stell ich mich auch nur blöd an, aber warum dann nicht
List<String> = new List<String>();
Wozu die unerwünschte Einschränkung dann noch rechts einbauen?
Hat das keinen Einfluss auf die Daten?
Benutzt man statt ArrayList oder LinkedList gleich List, legt sich also nicht auf eine konkrete Implementierung fest, dann braucht man auch nichts ändern, sondern es kann ArrayList und LinkedList übergeben werden. Man nennt das gegen Schnittstellen programmieren.
Hmm, so langsam wirds glaub heller
Und wenn ich dann intern weiter "einschränke", wie gebe ich das dann zurück? Wieder allgemein als List<String>?
Liege ich da völlig falsch, wenn ich sage es sei (ähnlich) wie beim Casten von int auf byte: solange der Inhalt in den Zahlenraum passt, ist es kein Problem?
Wer gibt wem was vor? Du meinst, wenn ich mir (von aussen) eine Liste an eine Methode übergeben lasse? Aber da kann ich doch sehr wohl Bedingungen stellen, oder etwa nicht? Immerhin entscheide ich mich ja für einen Typ, weil der mir gewisse (erwünschte) Vorteile bringt.
z.B. mir die WMFGabel noch die dringend benötigte Temperatur mit anzeigt. Wenn ich die nicht bräuchte, würde ich doch nicht eine WMF anfordern...
i.b.fan
Das ist im Prinzip richtig (und wurde von Wortraum ja auch erklärt). Wenn du "einen wirklich guten Grund" hast, eine ArrayList zu nehmen, dann nimm eine ArrayList. Aber in 99% der Fälle sollte es dir eben egal sein, und dann solltest du das auch sagen: "Mir ist schnuppe, was für eine Liste das ist". Dass du auch in diesem Fall "am Anfang" (auf der rechten Seite) mit irgendeiner konkreten Implementierung starten musst, sollte auch klar sein: Es gibt eben keine "Gabel an sich" (auch wenn Kant etwas anderes behauptet), nur konkrete Ausführungen.
List ist nur ein Interface, eine leere Hülle, eine Blaupause, ein Konzept, ein Art Vertrag. List sagt: "Alles was sich Liste schimpfen will, braucht eine add-Methode, die wie folgt aussieht..." In ArrayList steht "richtiger Code", der etwas tut, wenn man die add-Methode aufruft.
Wenn ich den o.g. Code also allgemeiner/flexibler halten will, wäre das dann so richtig?
Java:
publicclassEineKlasse{privatefinalList<String> strings;publicEineKlasse(List<String> strings){this.strings =(ArrayList<String>) strings;//bzw. wie mache ich daraus dann eine ArrayList?}publicList<String>getStrings(){return(List<String>) strings;}}
Das ist im Prinzip richtig (und wurde von Wortraum ja auch erklärt). Wenn du "einen wirklich guten Grund" hast, eine ArrayList zu nehmen, dann nimm eine ArrayList. Aber in 99% der Fälle sollte es dir eben egal sein, und dann solltest du das auch sagen: "Mir ist schnuppe, was für eine Liste das ist". Dass du auch in diesem Fall "am Anfang" (auf der rechten Seite) mit irgendeiner konkreten Implementierung starten musst, sollte auch klar sein: Es gibt eben keine "Gabel an sich" (auch wenn Kant etwas anderes behauptet), nur konkrete Ausführungen.
List ist nur ein Interface, eine leere Hülle, eine Blaupause, ein Konzept, ein Art Vertrag. List sagt: "Alles was sich Liste schimpfen will, braucht eine add-Methode, die wie folgt aussieht..." In ArrayList steht "richtiger Code", der etwas tut, wenn man die add-Methode aufruft.
Ok, ich glaube langsam dämmerts mir. (ich denke wohl noch zu sehr wie Kant )
Das heisst aber auch, dass ich mich innerhalb meiner Blackbox dann sehr wohl für einen Typ entscheiden muss. Und ich dafür Sorge tragen muss, die übergebene (allgemeinere) List<> in meinen Typ zu wandeln und auch z.B. Bedingungen (wie Dupletten o.ä.) zu prüfen.
Es verlagert sich quasi diese Arbeit von aussen ins innere der Blackbox. Sozusagen kundenfreundlich
Das leuchtet mir sehr wohl ein. Vielleicht stell ich mich auch nur blöd an, aber warum dann nicht
List<String> = new List<String>();
Wozu die unerwünschte Einschränkung dann noch rechts einbauen?
Hat das keinen Einfluss auf die Daten?
List ist eine Schnittstelle, davon kann man keine Objekte erzeugen. In einer Schnittstelle steht nichts weiter drin als die Methodensignaturen (die „Methodenköpfe“), die eine implementierende Klasse haben muß.
Die Zeilen 1 und 2 sagen ja erst einmal so viel: strings1 und strings2 sind vom Typ List. Du kannst damit alles machen, was du mit List machen kannst. Oder anders: alle Methoden, die es in List gibt, kann man auch bei strings1 und strings2 aufrufen.
Welches Objekt dahintersteht, ist egal, solange es mindestens das kann, was List kann. ArrayList implementiert List, LinkedList implementiert List, sie haben also mindestens die Methoden, die man in List findet. Java entscheidet zur Laufzeit, bei welchem Objekt diese Methode ausgeführt wird.
Zeile 3 könnte man so erklären: Nimm das Objekt, das „in“ strings1 steht, und führe dort size() auf. Zeile 5 macht exakt das gleiche, nur wurde in Zeile 4 vorher strings1 ein anderes Objekt zugewiesen, somit wird auch size() des anderen Objektes ausgeführt.
Man kann auch Tiere als Beispiel nehmen. Tiere kann man füttern, drücken und beobachten. Das kann man mit allen Tieren, egal, ob es nun ein Hund oder ein Kugelfisch ist.
Java:
Tier a;
a =newHund();
a.drücken();
a =newKugelfisch();
a.drücken();
Ein Tier kann man drücken [a.drücken()], und welches Tier sich nun tatsächlich hinter a verbirgt, entscheidet Java zur Laufzeit. Hier ist es erst ein Hund, dann ein Kugelfisch.
Natürlich kann man aber nicht sagen: Hund h = new Kugelfisch();
Einen Hund kann man vielleicht am Schwanz ziehen [h.zieheSchwanz()], aber der Kugelfisch, also das erzeugte Objekt, kann damit nichts anfangen. Ein Hund ist ein Tier, ein Kugelfisch ist Tier, aber ein Hund ist etwas ganz anderes als ein Kugelfisch. (Außer die fetten Hunde, die von alten Damen gezerrt werden, die sind Kugelfischen manchmal recht ähnlich. Aber nur äußerlich, Hund ist Hund und niemals Kugelfisch.)
[...] Welches Objekt dahintersteht, ist egal, solange es mindestens das kann, was List kann. ArrayList implementiert List, LinkedList implementiert List, sie haben also mindestens die Methoden, die man in List findet. [...]
Ah, das "mindestens" ist das Schlüsselwort für meine Blockade! *stirnpatsch*
[...] (Außer die fetten Hunde, die von alten Damen gezerrt werden, die sind Kugelfischen manchmal recht ähnlich. Aber nur äußerlich, Hund ist Hund und niemals Kugelfisch.)