Załóżmy na przykład, że masz dwie klasy:
public class TestA {}
public class TestB extends TestA{}
Mam metodę, która zwraca a List<TestA>
i chciałbym rzutować wszystkie obiekty z tej listy na, TestB
aby skończyć z List<TestB>
.
Po prostu casting do List<TestB>
prawie działa; ale to nie działa, ponieważ nie można rzutować typu ogólnego jednego parametru na inny. Możesz jednak użyć pośredniego typu symbolu wieloznacznego i będzie to dozwolone (ponieważ możesz przesyłać do i z typów symboli wieloznacznych, tylko z niezaznaczonym ostrzeżeniem):
List<TestB> variable = (List<TestB>)(List<?>) collectionOfListA;
rzutowanie ogólnych nie jest możliwe, ale jeśli zdefiniujesz listę w inny sposób, możesz w niej zapisać TestB:
Nadal masz sprawdzanie typu do zrobienia, gdy używasz obiektów z listy.
źródło
Cannot instantiate the type ArrayList<? extends TestA>
.Naprawdę nie możesz *:
Przykład pochodzi z tego samouczka Java
Załóżmy, że istnieją dwa typy
A
iB
takie, żeB extends A
. Następnie następujący kod jest poprawny:Poprzedni kod jest prawidłowy, ponieważ
B
jest podklasąA
. Co się teraz dzieje zList<A>
iList<B>
?Okazuje się, że
List<B>
nie jest to podklasa,List<A>
dlatego nie możemy pisaćCo więcej, nie możemy nawet pisać
*: Aby umożliwić casting, potrzebujemy wspólnego rodzica dla obu
List<A>
iList<B>
:List<?>
na przykład. Poniższa jest ważny:Otrzymasz jednak ostrzeżenie. Możesz go wyłączyć, dodając
@SuppressWarnings("unchecked")
do swojej metody.źródło
Z Javą 8 możesz to zrobić
źródło
new List<TestB>
pętlą i obsadą?Myślę, że rzucasz w złym kierunku ... jeśli metoda zwróci listę
TestA
obiektów, to naprawdę nie jest bezpiecznie ich rzucićTestB
.Zasadniczo prosisz kompilator o pozwolenie na wykonywanie
TestB
operacji na typach,TestA
które ich nie obsługują.źródło
Ponieważ jest to pytanie, na które często się powołuje, a obecne odpowiedzi głównie wyjaśniają, dlaczego to nie działa (lub proponują hackerskie, niebezpieczne rozwiązania, których nigdy nie chciałbym widzieć w kodzie produkcyjnym), uważam, że należy dodać kolejną odpowiedź, pokazującą pułapki i możliwe rozwiązanie.
Powód, dla którego to ogólnie nie działa, został już wskazany w innych odpowiedziach: To, czy konwersja jest rzeczywiście ważna, zależy od typów obiektów zawartych na oryginalnej liście. Gdy istnieją obiekty w liście, którego typ nie jest typu
TestB
, ale o innej podklasyTestA
, następnie obsada jest nie ważna.Oczywiście, obsady mogą być ważne. Czasami masz informacje o typach, które nie są dostępne dla kompilatora. W takich przypadkach możliwe jest przesyłanie list, ale ogólnie nie jest zalecane :
Można albo ...
Implikacje pierwszego podejścia (które odpowiadają obecnie przyjętej odpowiedzi) są subtelne. Na pierwszy rzut oka może się wydawać, że działa poprawnie. Ale jeśli na liście wejściowej znajdują się niewłaściwe typy, to
ClassCastException
zostanie wyrzucony, być może w zupełnie innym miejscu w kodzie, i może być trudno go debugować i dowiedzieć się, gdzie niewłaściwy element wślizgnął się na listę. Najgorszym problemem jest to, że ktoś może nawet dodać nieprawidłowe elementy po rzutowaniu listy, co jeszcze bardziej utrudnia debugowanie.Problem debugowania tych fałszywych
ClassCastExceptions
można złagodzić dziękiCollections#checkedCollection
rodzinie metod.Filtrowanie listy na podstawie typu
Bardziej bezpiecznym sposobem na konwersję z a
List<Supertype>
na aList<Subtype>
jest przefiltrowanie listy i utworzenie nowej listy zawierającej tylko elementy określonego typu. Istnieją pewne stopnie swobody we wdrażaniu takiej metody (np. W odniesieniu do traktowanianull
wpisów), ale jedna możliwa implementacja może wyglądać następująco:Metodę tę można zastosować do filtrowania dowolnych list (nie tylko z daną relacją podtypu i typu pod względem parametrów typu), jak w tym przykładzie:
źródło
Nie można rzucać
List<TestB>
sięList<TestA>
jak wspomina Steve Kuo ale można zrzucić zawartośćList<TestA>
doList<TestB>
. Spróbuj wykonać następujące czynności:Nie próbowałem tego kodu, więc prawdopodobnie są to błędy, ale pomysł polega na tym, że powinien on iterować przez obiekt danych dodając elementy (obiekty TestB) do listy. Mam nadzieję, że ci się uda.
źródło
Najlepszym bezpiecznym sposobem jest zaimplementowanie
AbstractList
i rzucenie przedmiotów w trakcie implementacji. StworzyłemListUtil
klasę pomocnika:Możesz użyć
cast
metody ślepego rzutowania obiektów na liście orazconvert
metody bezpiecznego rzutowania. Przykład:źródło
Jedyny sposób, jaki znam to kopiowanie:
źródło
Kiedy rzutujesz odwołanie do obiektu, po prostu rzutujesz typ odwołania, a nie typ obiektu. rzutowanie nie zmieni faktycznego typu obiektu.
Java nie ma niejawnych reguł konwersji typów obiektów. (W przeciwieństwie do prymitywów)
Zamiast tego musisz podać, jak przekonwertować jeden typ na inny i wywołać go ręcznie.
Jest to bardziej szczegółowe niż w języku z bezpośrednim wsparciem, ale działa i nie powinieneś robić tego zbyt często.
źródło
jeśli masz obiekt klasy
TestA
, nie możesz go rzucićTestB
. każdyTestB
jestTestA
, ale nie na odwrót.w następującym kodzie:
druga linia rzuciłaby
ClassCastException
.możesz rzucić
TestA
odwołanie tylko wtedy, gdy sam obiekt jestTestB
. na przykład:więc nie zawsze możesz rzucić listę
TestA
na listęTestB
.źródło
Możesz użyć tej
selectInstances
metody w kolekcji Eclipse . Będzie to wiązało się z utworzeniem nowej kolekcji, jednak nie będzie tak wydajne, jak zaakceptowane rozwiązanie wykorzystujące rzutowanie.Podałem
StringBuffer
w przykładzie, aby pokazać, żeselectInstances
nie tylko obniża typ, ale także filtruje, jeśli kolekcja zawiera typy mieszane.Uwaga: jestem osobą odpowiedzialną za kolekcje Eclipse.
źródło
Jest to możliwe z powodu usunięcia typu. Znajdziesz to
Wewnętrznie obie listy są typu
List<Object>
. Z tego powodu nie możesz rzucać jednego na drugi - nie ma nic do rzucenia.źródło
Integer j = 42; Integer i = (Integer) j;
działa dobrze, oba są liczbami całkowitymi, oba mają tę samą klasę, więc „nie ma co rzucać”.Problem polega na tym, że twoja metoda NIE zwraca listy TestA, jeśli zawiera TestB, więc co, jeśli została poprawnie wpisana? Następnie ta obsada:
działa tak dobrze, jak można się spodziewać (Eclipse ostrzega przed niesprawdzoną obsadą, która jest dokładnie tym, co robisz, więc meh). Czy możesz to wykorzystać do rozwiązania swojego problemu? W rzeczywistości możesz z tego powodu:
Dokładnie o co prosiłeś, prawda? lub być naprawdę dokładnym:
wydaje się, że dobrze się kompiluje.
źródło
Ten test pokazuje, jak przesyłać
List<Object>
naList<MyClass>
. Ale musisz zwrócić uwagę na to, żeobjectList
musi zawierać instancje tego samego typu coMyClass
. I ten przykład można rozważyć, gdyList<T>
jest używany. W tym celu pobierz poleClass<T> clazz
w konstruktorze i użyj go zamiastMyClass.class
.źródło
To powinno zadziałać
źródło
Dość dziwne, że ręczne przesyłanie listy wciąż nie jest dostępne w niektórych narzędziach implementujących coś takiego:
Oczywiście nie będzie to sprawdzać pozycji jeden po drugim, ale właśnie tego chcemy tutaj uniknąć, jeśli dobrze wiemy, że nasza implementacja zapewnia tylko podtyp.
źródło