Załóżmy, że mam tę hierarchię klas ...
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() {
}
}
A potem mam ....
public static <T extends Animal> void addAnimal(T animal) {
animal.eat();
animal.talk();
}
public static void addAnimalPoly(Animal animal) {
animal.eat();
animal.talk();
}
Jaka jest różnica przy stosowaniu parametrów typu ograniczonego lub polimorfizmu?
A kiedy używać jednego lub drugiego?
java
polymorphism
generics
HugoMelo
źródło
źródło
addAnimals(List<Animal>)
i dodać Listę kotów!Odpowiedzi:
Te dwa przykłady są równoważne i faktycznie skompilują się do tego samego kodu bajtowego.
Istnieją dwa sposoby dodania ograniczonego typu ogólnego do metody, tak jak w pierwszym przykładzie.
Przekazywanie parametru type do innego typu
Te dwie sygnatury metod są takie same w kodzie bajtów, ale kompilator wymusza bezpieczeństwo typu:
public static <T extends Animal> void addAnimals(Collection<T> animals)
public static void addAnimals(Collection<Animal> animals)
W pierwszym przypadku dozwolony jest tylko
Collection
(lub podtyp)Animal
. W drugim przypadku dozwolony jestCollection
(lub podtyp) o typie ogólnymAnimal
lub podtyp.Na przykład dozwolone są w pierwszej metodzie, ale nie w drugiej:
Powodem jest to, że drugi zezwala tylko na kolekcje zwierząt, podczas gdy pierwszy zezwala na kolekcje dowolnego obiektu, który można przypisać zwierzęciu (tj. Podtypom). Zauważ, że jeśli ta lista byłaby listą zwierząt, które zawierały kota, każda metoda by to zaakceptowała: problemem jest ogólna specyfikacja kolekcji, a nie jej zawartość.
Zwracane przedmioty
Drugi czas ma znaczenie w przypadku zwracanych przedmiotów. Załóżmy, że istniała następująca metoda:
Można dzięki temu wykonać następujące czynności:
Chociaż jest to wymyślony przykład, istnieją przypadki, w których ma to sens. Bez generycznych metod metoda musiałaby powrócić
Animal
i trzeba by dodać rzutowanie typu, aby działało (co kompilator dodaje do kodu bajtu za kulisami).źródło
Używaj ogólnych zamiast downcastingu. „Downcasting” jest zły, przechodząc z bardziej ogólnego typu na bardziej szczegółowy:
... ufasz, że
a
to kot, ale kompilator nie może tego zagwarantować. Może się okazać, że jest to pies w czasie wykonywania.Oto, gdzie możesz użyć ogólnych:
Teraz możesz określić, że chcesz łowcę kotów:
Teraz kompilator może Gwarantujemy że hunterC przechwyci tylko koty i psy hunterD tylko przechwytywania.
Używaj więc zwykłego polimorfizmu, jeśli chcesz obsługiwać określone klasy jako ich podstawowy typ. Upcasting to dobra rzecz. Ale jeśli znajdziesz się w sytuacji, w której musisz obsługiwać określone klasy jako ich własne typy, na ogół używaj rodzajów ogólnych.
Lub, naprawdę, jeśli okaże się, że musisz spuszczać, skorzystaj z generycznych.
EDYCJA: bardziej ogólny przypadek ma miejsce, gdy chcesz odłożyć decyzję, jakie rodzaje typów obsługiwać. Zatem typy stają się parametrem, a także wartościami.
Powiedz, że chcę, aby moja klasa Zoo zajmowała się Kotami lub Gąbkami. Nie mam wspólnej super klasy. Ale nadal mogę używać:
stopień, w jakim to zablokujesz, zależy od tego, co próbujesz zrobić;)
źródło
To pytanie jest stare, ale wydaje się, że pominięto ważny czynnik dotyczący tego, kiedy użyć polimorfizmu w porównaniu do parametrów typu ograniczonego. Ten czynnik może być nieco styczny do przykładu podanego w pytaniu, ale wydaje mi się, że jest bardzo istotny dla bardziej ogólnego „Kiedy stosować polimorfizm a parametry typu ograniczonego?”
TL; DR
Jeśli kiedykolwiek zdarzy ci się przenieść kod z podklasy do klasy bazowej wbrew lepszej ocenie, z powodu niemożności uzyskania dostępu do niego w sposób polimorficzny, parametry typu ograniczonego mogą być potencjalnym rozwiązaniem.
Pełna odpowiedź
Parametry typu ograniczonego mogą ujawniać konkretne, nie odziedziczone metody podklasy dla dziedziczonej zmiennej elementu. Polimorfizm nie może
Aby rozwinąć, rozszerzając swój przykład:
Jeśli abstrakcyjna klasa AnimalOwner została zdefiniowana jako posiadająca
protected Animal pet;
i wybierająca polimorfizm, kompilator popełni błąd wpet.scratchBelly();
linii, informując, że ta metoda jest niezdefiniowana dla Animal.źródło
W twoim przykładzie nie (i nie powinieneś) używać ograniczonego typu. Używaj parametrów ograniczonego typu tylko wtedy, gdy musisz , ponieważ są bardziej mylące ze zrozumieniem.
Oto kilka sytuacji, w których użyjesz parametrów typu ograniczonego:
Parametry kolekcji
wtedy możesz zadzwonić
zoo.add(dogs)
nie udało się skompilować bez<? extends Animal>
, ponieważ generyczne nie są kowariantne.Podklasowanie
w celu ograniczenia podklasy typu może zapewnić.
Możesz także użyć wielu granic,
<T extends A1 & A2 & A3>
aby upewnić się, że typ jest podtypem wszystkich typów na liście.źródło