Przypuśćmy, że mamy klasę A, a klasa B, która dziedziczy z klasy A. Powiedzmy, że mamy:

Set<A> setOfAs = new HashSet<>();

Następujące odlewanie:

((Set<B>) setOfAs)

Daje nam błąd czasu wykonywania.

Jeśli jednak używamy wieloznacznego i zdefiniować następujący zestaw:

Set<? extends A> setOfAs = new HashSet<>();

Nie mamy problemu z wytworzeniem odlewania:

((Set<B>) setOfAs)

Dlaczego dozwolony jest odlewanie kolekcji wieloznacznej, podczas rzucania kolekcji "regularny" jest zabroniony?

5
CrazySynthax 25 czerwiec 2020, 12:02

2 odpowiedzi

Nie mamy problemu z wytworzeniem odlewania:

Będziesz mieć niezaznaczone ostrzeżenie, więc tak naprawdę nie masz problemu; Po prostu kompilator nie może udowodnić, że to zdecydowanie się myląc i nie może umieścić niczego do byteCode, aby złapać fakt, że jest nie tak w czasie wykonywania.

A Set<? extends T> jest Set, gdzie można założyć, że wszyscy członkowie mogą być bezpiecznie odlewać do T bez ClassCastException.

A Set<? super T> byłoby Set, gdzie wiadomo, że jest bezpieczny, aby dodać T bez powodowania ClassCastException w miejscach opierających się na typach elementów w {{ X4}} Uważam, że prawidłowy termin techniczny dla tego jest bez powodowania zanieczyszczenia sterty ).

Set<T> jest przecięciem tych dwóch ograniczonych typów: można dodać instancje T do niego, a wszystkie elementy są instancjami T.

Te definicje, Set<B> może działać jako Set<? extends A>, ponieważ wszystko, co można rzucić do B może być również rzucony do A.

Jednakże, Set<A> nie może działać jako Set<B>, ponieważ może zawierać instancje A, które nie są instancjami B.

3
Andy Turner 25 czerwiec 2020, 09:30

Cała idea odlewania mówi: "Wiem więcej niż ty, kompilator!" Kiedy istnieje pewna niepewność co do rzeczywistego rodzaju obiektu.

W drugim przypadku sprawia, że sens. Kompilator wie, że setOfAs jest typu Set<? extends A>, co oznacza "A Set nieznanego typu, a ten nieznany typ rozciąga się A". Nie jest niepewność co typy HashSet może być. Jeśli chodzi o kompilator, to może być HashSet<B> ...

Set<? extends A> setOfAs = new HashSet<A>();

Ale to także może być HashSet<A> ...

Set<? extends A> setOfAs = new HashSet<B>();

Ty, za pomocą odlewania, powiedzmy "Nie, setOfAs jest HashSet<B>". Kompilator idzie: "Cóż, to może być tak, więc zaufam ci". Niezależnie od tego, czy twoja dodatkowa wiedza jest faktycznie poprawna, jest osobną sprawą.

W pierwszym przypadku jednak setofAs jest typu HashSet<A>. Ponieważ zmienna typu HashSet<A> może nigdy przechowuj obiekt typu HashSet<B>, tj. Nie spotyka się to:

Set<A> setOfAs = new HashSet<B>();

Nie ma niepewności co do ogólnego parametru Set. Muszę być A. Próbując rzucić HashSet<B>, doprowadzi tylko do kompilatora mówiącego "Nie, to może nigdy więcej!"

1
Sweeper 25 czerwiec 2020, 09:15