Angenommen, ich habe diese Klassenhierarchie ...
public abstract class Animal {
public abstract void eat();
public abstract void talk();
}
class Dog extends Animal {
@Override
public void eat() {
}
@Override
public void talk() {
}
}
class Cat extends Animal {
@Override
public void eat() {
}
@Override
public void talk() {
}
}
Und dann habe ich ....
public static <T extends Animal> void addAnimal(T animal) {
animal.eat();
animal.talk();
}
public static void addAnimalPoly(Animal animal) {
animal.eat();
animal.talk();
}
Was ist der Unterschied bei Verwendung von begrenzten Typparametern oder Polymorphismus?
Und wann benutzt man das eine oder andere?
java
polymorphism
generics
HugoMelo
quelle
quelle
addAnimals(List<Animal>)
eine Liste der Katzen zu schreiben und hinzuzufügen!Antworten:
Diese beiden Beispiele sind äquivalent und werden tatsächlich mit demselben Bytecode kompiliert.
Es gibt zwei Möglichkeiten, einer Methode einen begrenzten generischen Typ hinzuzufügen, wie im ersten Beispiel beschrieben.
Übergeben des Typparameters an einen anderen Typ
Diese beiden Methodensignaturen sind im Bytecode identisch, der Compiler erzwingt jedoch die Typensicherheit:
public static <T extends Animal> void addAnimals(Collection<T> animals)
public static void addAnimals(Collection<Animal> animals)
Im ersten Fall ist nur ein
Collection
(oder ein Subtyp) vonAnimal
zulässig. Im zweiten Fall ist einCollection
(oder ein Subtyp) mit einem generischen TypAnimal
oder einem Subtyp zulässig.Beispielsweise ist in der ersten Methode Folgendes zulässig, in der zweiten jedoch nicht:
Der Grund ist, dass der zweite nur Sammlungen von Tieren zulässt, während der erste Sammlungen von Objekten zulässt, die einem Tier zugeordnet werden können (dh Subtypen). Wenn diese Liste eine Liste von Tieren wäre, die zufällig eine Katze enthielten, würde dies von beiden Methoden akzeptiert: Das Problem ist die generische Spezifikation der Sammlung, nicht das, was sie tatsächlich enthält.
Gegenstände zurückgeben
Das andere Mal ist es wichtig, Objekte zurückzugeben. Nehmen wir an, dass folgende Methode existiert:
Damit könnten Sie Folgendes tun:
Obwohl dies ein erfundenes Beispiel ist, gibt es Fälle, in denen es sinnvoll ist. Ohne Generika müsste die Methode zurückkehren
Animal
und Sie müssten Typumwandlung hinzufügen, damit sie funktioniert (was der Compiler hinter den Kulissen sowieso zum Bytecode hinzufügt).quelle
Verwenden Sie Generika anstelle von Downcasting. "Downcasting" ist schlecht und geht von einem allgemeineren Typ zu einem spezifischeren über:
... Sie vertrauen darauf, dass
a
es sich um eine Katze handelt, aber der Compiler kann dies nicht garantieren. Es könnte sich zur Laufzeit herausstellen, dass es sich um einen Hund handelt.Hier ist, wo Sie Generika verwenden würden:
Jetzt können Sie festlegen, dass Sie einen Katzenjäger wollen:
Jetzt kann der Compiler garantieren, dass hunterC nur Katzen und hunterD nur Hunde erfasst.
Verwenden Sie also einfach den regulären Polymorphismus, wenn Sie nur bestimmte Klassen als Basistyp verwenden möchten. Upcasting ist eine gute Sache. Aber wenn Sie in einer Situation sind, in der Sie bestimmte Klassen als ihren eigenen Typ behandeln müssen, verwenden Sie generische Klassen.
Oder wirklich, wenn Sie feststellen, dass Sie niedergeschlagen werden müssen, verwenden Sie Generika.
BEARBEITEN: Der allgemeinere Fall ist, wenn Sie die Entscheidung darüber aufschieben möchten, welche Arten von Typen behandelt werden sollen. So werden sowohl die Typen als auch die Werte zu Parametern.
Angenommen, ich möchte, dass meine Zoo-Klasse mit Katzen oder Schwämmen umgeht. Ich habe keine gemeinsame Superklasse. Aber ich kann immer noch verwenden:
Inwieweit Sie dies sperren, hängt davon ab, was Sie tun möchten.
quelle
Diese Frage ist altmodisch, aber ein wichtiger Faktor, der zu berücksichtigen ist, scheint in Bezug auf die Verwendung von Polymorphismus gegenüber begrenzten Typparametern ausgelassen worden zu sein. Dieser Faktor mag sich leicht auf das in der Frage angegebene Beispiel auswirken, ist jedoch meiner Meinung nach für das allgemeinere Thema "Wann ist Polymorphismus gegenüber begrenzten Typparametern anzuwenden?" Von großer Bedeutung.
TL; DR
Wenn Sie jemals feststellen sollten, dass Sie den Code von einer Unterklasse in eine Basisklasse verschieben, weil Sie nicht mehr polymorph darauf zugreifen können, könnten begrenzte Typparameter eine mögliche Lösung sein.
Die vollständige Antwort
Mit begrenzten Typparametern können konkrete, nicht vererbte Unterklassenmethoden für eine vererbte Elementvariable verfügbar gemacht werden. Polymorphismus kann nicht
So erweitern Sie Ihr Beispiel:
Wenn für die abstrakte Klasse AnimalOwner ein
protected Animal pet;
und für Polymorphismus festgelegt wurde, gibt der Compiler in derpet.scratchBelly();
Zeile einen Fehler aus und teilt Ihnen mit, dass diese Methode für Animal nicht definiert ist.quelle
In Ihrem Beispiel verwenden Sie keinen begrenzten Typ (und sollten ihn auch nicht verwenden). Verwenden Sie nur begrenzte Typparameter, wenn dies erforderlich ist , da das Verständnis der Parameter verwirrender ist.
In der folgenden Situation verwenden Sie begrenzte Typparameter:
Sammlungsparameter
dann kannst du anrufen
zoo.add(dogs)
würde nicht ohne kompilieren<? extends Animal>
, weil generika nicht kovariant sind.Unterklassen
um den Typ einzuschränken, den die Unterklasse bereitstellen kann.
Sie können auch mehrere Schranken verwenden,
<T extends A1 & A2 & A3>
um sicherzustellen, dass ein Typ ein Untertyp aller Typen in der Liste ist.quelle