Ich sehe, dass die Frage bereits mehrfach beantwortet wurde, möchte aber dennoch meine Meinung zu dieser Frage äußern.
Lassen Sie uns nun eine vereinfachte Hierarchie der Tierklassen erstellen.
abstract class Animal {
void eat() {
System.out.println("animal eating");
}
}
class Dog extends Animal {
void bark() { }
}
class Cat extends Animal {
void meow() { }
}
Werfen wir nun einen Blick auf unseren alten Freund Arrays, von dem wir wissen, dass er implizit Polymorphismus unterstützt -
class TestAnimals {
public static void main(String[] args) {
Animal[] animals = {new Dog(), new Cat(), new Dog()};
Dog[] dogs = {new Dog(), new Dog(), new Dog()};
takeAnimals(animals);
takeAnimals(dogs);
}
public void takeAnimals(Animal[] animals) {
for(Animal a : animals) {
System.out.println(a.eat());
}
}
}
Die Klasse lässt sich gut kompilieren, und wenn wir die obige Klasse ausführen, erhalten wir die folgende Ausgabe
animal eating
animal eating
animal eating
animal eating
animal eating
animal eating
Hier ist zu beachten, dass die takeAnimals()-Methode so definiert ist, dass sie alles aufnehmen kann, was vom Typ Tier ist, sie kann ein Array vom Typ Tier aufnehmen und sie kann auch ein Array von Hund aufnehmen, weil Hund-ist-ein-Tier. Dies ist also Polymorphismus in Aktion.
Lassen Sie uns nun den gleichen Ansatz mit Generika anwenden,
Ändern wir nun unseren Code ein wenig und verwenden ArrayLists anstelle von Arrays -
class TestAnimals {
public static void main(String[] args) {
ArrayList<Animal> animals = new ArrayList<Animal>();
animals.add(new Dog());
animals.add(new Cat());
animals.add(new Dog());
takeAnimals(animals);
}
public void takeAnimals(ArrayList<Animal> animals) {
for(Animal a : animals) {
System.out.println(a.eat());
}
}
}
Die obige Klasse wird kompiliert und erzeugt die Ausgabe -
animal eating
animal eating
animal eating
animal eating
animal eating
animal eating
Wir wissen also, dass es funktioniert. Nun können wir diese Klasse ein wenig anpassen, um den Typ Animal polymorph zu verwenden.
class TestAnimals {
public static void main(String[] args) {
ArrayList<Animal> animals = new ArrayList<Animal>();
animals.add(new Dog());
animals.add(new Cat());
animals.add(new Dog());
ArrayList<Dog> dogs = new ArrayList<Dog>();
takeAnimals(animals);
takeAnimals(dogs);
}
public void takeAnimals(ArrayList<Animal> animals) {
for(Animal a : animals) {
System.out.println(a.eat());
}
}
}
Es sollte kein Problem bei der Kompilierung der obigen Klasse geben, da die takeAnimals()-Methode so konzipiert ist, dass sie jede ArrayList des Typs Animal und Dog-is-a-Animal aufnehmen kann, so dass dies hier kein Problem darstellen sollte.
Aber leider wirft der Compiler einen Fehler und erlaubt uns nicht, eine Dog ArrayList an eine Variable zu übergeben, die Animal ArrayList erwartet.
Sie fragen warum?
Stellen Sie sich vor, JAVA würde es zulassen, dass die ArrayList "Dog" - Hunde - in die ArrayList "Animal" - Tiere - eingefügt wird, und in der takeAnimals()-Methode macht jemand etwas wie -
animals.add(new Cat());
denken, dass dies machbar sein sollte, weil es idealerweise ein Tier ArrayList ist und Sie in der Lage sein sollten, jede Katze hinzuzufügen, wie Katze-ist-auch-ein-Tier, aber in Wirklichkeit übergeben Sie einen Hund-Typ ArrayList es.
Jetzt denken Sie sicher, dass dasselbe auch mit den Arrays hätte passieren müssen. Sie haben Recht, wenn Sie so denken.
Wenn jemand versucht, das Gleiche mit Arrays zu tun, dann werden Arrays auch einen Fehler auslösen, aber Arrays behandeln diesen Fehler zur Laufzeit, während ArrayLists diesen Fehler zur Kompilierzeit behandeln.
22 Stimmen
Und eine völlig unabhängige Grammatikfrage, die mich gerade beschäftigt - sollte mein Titel "warum" lauten? sind nicht Javas Generika" oder "Warum ist nicht Java's generics"? Ist "Generika" Plural wegen des s oder Singular, weil es eine Einheit ist?
28 Stimmen
Generika, wie sie in Java verwendet werden, sind eine sehr schlechte Form des parametrischen Polymorphismus. Setzen Sie nicht zu viel Vertrauen in sie (wie ich es früher getan habe), denn eines Tages werden Sie hart an ihre erbärmlichen Grenzen stoßen: Surgeon extends Handable<Scalpel>, Handable<Sponge> KABOOM! Macht nicht berechnen [TM]. Das ist die Einschränkung der Java-Generik. Jede OOA/OOD kann gut in Java übersetzt werden (und MI kann sehr schön mit Java-Schnittstellen gemacht werden), aber Generika sind einfach nicht geeignet. Sie sind gut für "Sammlungen" und prozedurale Programmierung, dass sagte (das ist, was die meisten Java-Programmierer sowieso tun, so...).
8 Stimmen
Die Superklasse von List<Dog> ist nicht List<Animal>, sondern List<?> (d. h. eine Liste unbekannten Typs). Generics löscht Typinformationen im kompilierten Code. Dies geschieht, damit Code, der Generics verwendet (Java 5 und höher), mit früheren Versionen von Java ohne Generics kompatibel ist.
5 Stimmen
Verwandte SO-Fragen - Was nützt es, <? extends SomeObject> statt <SomeObject> zu sagen?
0 Stimmen
Ich möchte nur hinzufügen, dass es ein offizielles Java-Tutorial zu diesem Thema gibt: docs.oracle.com/javase/tutorial/extra/generics/subtype.html
10 Stimmen
@froadie, da niemand zu antworten schien... es sollte definitiv heißen "warum sind Javas Generics nicht...". Das andere Problem ist, dass "generisch" eigentlich ein Adjektiv ist und "Generika" sich auf ein fallengelassenes Pluralnomen bezieht, das durch "generisch" modifiziert wird. Man könnte sagen "diese Funktion ist eine generische", aber das wäre umständlicher als "diese Funktion ist generisch". Allerdings ist es auch etwas umständlich, zu sagen "Java hat generische Funktionen und Klassen", anstatt einfach "Java hat Generika". Als jemand, der seine Magisterarbeit über Adjektive geschrieben hat, denke ich, dass Sie über eine sehr interessante Frage gestolpert sind!
1 Stimmen
List<A>
hat keine Beziehung zuList<B>
unabhängig von der Beziehung zwischen den Klassen A und B.0 Stimmen
@dantiston Generics könnte als kollektiver Singular betrachtet werden, in diesem Fall funktioniert "isn't" sehr gut.
0 Stimmen
@SyntaxT3rr0r In Ihrem Beispiel würde meine Lösung darin bestehen, eine Superschnittstelle von
Scalpel
ySponge
(wahrscheinlich mit einem Namen wieSurgeonItem
), dann deklariertSurgeon extends Handable<SurgeonItem>
.1 Stimmen
@rai.skumar
List<?>
ist nicht der einzige wirksame Supertyp vonList<Dog>
. Andere Beispiele sindList<? extends Dog>
yList<? super Dog>
. Außerdem kann jede Variante der vier (<Dog>
,<? super Dog>
,<? extends Dog>
,<?>
), angewandt auf einen Supertyp vonList
gelten ebenfalls:Collection<Dog>
,Iterable<? super Dog>
, usw.. Überall, wo Sie einen Verweis auf eines dieser Elemente haben, können Sie auch eineList<Dog>
. AuchList<? extends Animal>
yList<? super Chihuahua>
.1 Stimmen
Verwandt: Was ist PECS (Producer Extends Consumer Super)?