To się nie kompiluje, każda sugestia jest mile widziana.
...
List<Object> list = getList();
return (List<Customer>) list;
Kompilator mówi: nie można przesyłać List<Object>
doList<Customer>
To się nie kompiluje, każda sugestia jest mile widziana.
...
List<Object> list = getList();
return (List<Customer>) list;
Kompilator mówi: nie można przesyłać List<Object>
doList<Customer>
Odpowiedzi:
zawsze możesz rzutować dowolny obiekt na dowolny typ, przesyłając go najpierw do Object. w Twoim przypadku:
(List<Customer>)(Object)list;
musisz mieć pewność, że w czasie wykonywania lista zawiera tylko obiekty Customer.
Krytycy twierdzą, że takie rzutowanie wskazuje na coś złego w twoim kodzie; powinieneś być w stanie dostosować swoje deklaracje typu, aby tego uniknąć. Ale generics Java jest zbyt skomplikowany i nie jest doskonały. Czasami po prostu nie wiesz, czy istnieje ładne rozwiązanie, które zadowoli kompilatora, mimo że bardzo dobrze znasz typy środowiska uruchomieniowego i wiesz, że to, co próbujesz zrobić, jest bezpieczne. W takim przypadku po prostu wykonaj surowy odlew w razie potrzeby, abyś mógł wyjść z pracy do domu.
@SuppressWarnings("unchecked")
. Zauważ, że możesz także przesyłać do (List)
zamiast do (Object)
.
Dzieje się tak, ponieważ chociaż klient jest obiektem , lista klientów nie jest listą obiektów. Gdyby tak było, to można by umieścić dowolny obiekt na liście Klientów.
.Cast<T>()
i drugą wywołaną .OfType<T>()
. Pierwsza z nich wykonuje rzut na każdym elemencie (rzucając pożądane wyjątki), podczas gdy druga odfiltrowuje elementy, których nie można rzucić (więc wybierz jeden w zależności od scenariusza użycia).
instanceof
Klientem?
W zależności od innego kodu najlepsza odpowiedź może się różnić. Próbować:
List<? extends Object> list = getList();
return (List<Customer>) list;
lub
List list = getList();
return (List<Customer>) list;
Pamiętaj jednak, że nie zaleca się wykonywania takich niekontrolowanych rzutów.
Ze strumieniami Java 8 :
Czasami rzucanie siłą jest w porządku:
List<MyClass> mythings = (List<MyClass>) (Object) objects
Ale oto bardziej wszechstronne rozwiązanie:
List<Object> objects = Arrays.asList("String1", "String2");
List<String> strings = objects.stream()
.map(element->(String) element)
.collect(Collectors.toList());
Jest mnóstwo korzyści, ale jedną z nich jest to, że możesz bardziej elegancko przesyłać swoją listę, jeśli nie masz pewności, co zawiera:
objects.stream()
.filter(element->element instanceof String)
.map(element->(String)element)
.collect(Collectors.toList());
FluentIterable
na mnie zadziałała.
Możesz użyć podwójnej obsady.
return (List<Customer>) (List) getList();
Zauważ, że nie jestem programistą Java, ale w .NET i C # ta funkcja nazywa się kontrawariancją lub kowariancją. Nie zagłębiłem się jeszcze w te rzeczy, ponieważ są one nowe w .NET 4.0, którego nie używam, ponieważ jest to tylko beta, więc nie wiem, który z dwóch terminów opisuje twój problem, ale pozwól mi opisać problem techniczny z tym.
Załóżmy, że pozwolono ci rzucać. Zwróć uwagę, mówię rzutuj , ponieważ to właśnie powiedziałeś, ale są dwie operacje, które mogą być możliwe, rzutowanie i konwersja .
Konwersja oznaczałaby, że otrzymujesz nowy obiekt listy, ale mówisz, że rzutowanie, co oznacza, że chcesz tymczasowo traktować jeden obiekt jako inny typ.
Oto problem z tym.
Co by się stało, gdyby było dozwolone (uwaga, zakładam, że przed rzutem lista obiektów faktycznie zawiera tylko obiekty klienta, w przeciwnym razie rzut nie działałby nawet w tej hipotetycznej wersji java):
List<Object> list = getList();
List<Customer> customers = (List<Customer>)list;
list.Insert(0, new someOtherObjectNotACustomer());
Customer c = customers[0];
W takim przypadku spowoduje to próbę potraktowania obiektu, który nie jest klientem, jako klienta, aw pewnym momencie wystąpi błąd w czasie wykonywania, albo z formularza wewnątrz listy, albo z przypisania.
Jednak typy generyczne mają zapewniać typy danych bezpieczne dla typów, takie jak kolekcje, a ponieważ lubią rzucać słowo „gwarantowane”, tego rodzaju rzutowanie wraz z następującymi problemami jest niedozwolone.
W .NET 4.0 (wiem, twoje pytanie dotyczyło javy), będzie to dozwolone w bardzo szczególnych przypadkach , w których kompilator może zagwarantować, że wykonywane operacje są bezpieczne, ale w ogólnym sensie ten typ rzutowania nie będzie mieć pozwolenie. To samo odnosi się do języka Java, chociaż nie jestem pewien co do planów wprowadzenia współ- i kontrawariancji do języka Java.
Mam nadzieję, że ktoś z lepszą znajomością języka Java niż ja może opowiedzieć o szczegółach dotyczących przyszłości lub implementacji Java.
Innym podejściem byłoby użycie strumienia Java 8.
List<Customer> customer = myObjects.stream()
.filter(Customer.class::isInstance)
.map(Customer.class::cast)
.collect(toList());
List<Customer> cusList = new ArrayList<Customer>();
for(Object o: list){
cusList.add((Customer)o);
}
return cusList;
list.stream().forEach(x->cusList.add((Customer)x))
return cuslist;
Nie możesz, ponieważ List<Object>
i List<Customer>
nie są w tym samym drzewie dziedziczenia.
Możesz dodać nowy konstruktor do swojej List<Customer>
klasy, który pobiera a, List<Object>
a następnie iterować po liście, rzutując każdy Object
na a Customer
i dodając go do swojej kolekcji. Należy pamiętać, że nieprawidłowy wyjątek rzutowania może wystąpić, jeśli obiekt wywołujący List<Object>
zawiera coś, co nie jest plikiem Customer
.
Celem list ogólnych jest ograniczenie ich do określonych typów. Próbujesz wziąć listę, która może zawierać cokolwiek (zamówienia, produkty itp.) I wcisnąć ją do listy, która może zawierać tylko klientów.
Najlepszym rozwiązaniem jest utworzenie nowego List<Customer>
, iteracyjne przeglądanie List<Object>
, dodanie każdej pozycji do nowej listy i zwrócenie jej.
Jak zauważyli inni, nie możesz ich bezpiecznie rzucać, ponieważ a List<Object>
nie jest List<Customer>
. To, co możesz zrobić, to zdefiniować widok na liście, który sprawdza typ w miejscu. Korzystając z Google Kolekcje , byłoby to:
return Lists.transform(list, new Function<Object, Customer>() {
public Customer apply(Object from) {
if (from instanceof Customer) {
return (Customer)from;
}
return null; // or throw an exception, or do something else that makes sense.
}
});
Podobnie z Bozho powyżej. Możesz obejść ten problem tutaj (chociaż mi się to nie podoba) za pomocą tej metody:
public <T> List<T> convert(List list, T t){
return list;
}
Tak. Spowoduje to rzutowanie listy na wymagany typ ogólny.
W podanym powyżej przypadku możesz wykonać taki kod:
List<Object> list = getList();
return convert(list, new Customer());
W zależności od tego, co chcesz zrobić z listą, może nawet nie być konieczne przesyłanie jej do pliku List<Customer>
. Jeśli chcesz tylko dodać Customer
obiekty do listy, możesz zadeklarować to w następujący sposób:
...
List<Object> list = getList();
return (List<? super Customer>) list;
Jest to legalne (no cóż, nie tylko legalne, ale poprawne - lista jest „jakiegoś nadtypu dla Klienta”) i jeśli zamierzasz przekazać ją do metody, która będzie jedynie dodawać obiekty do listy, to powyższe wystarczą do tego ogólne granice.
Z drugiej strony, jeśli chcesz pobrać obiekty z listy i mieć je silnie wpisane jako Klienci - to nie masz szczęścia i słusznie. Ponieważ lista jest listą, List<Object>
nie ma gwarancji, że zawartość jest klientem, więc będziesz musiał zapewnić własny rzut przy pobieraniu. (Lub bądź naprawdę, absolutnie, podwójnie pewny, że lista będzie zawierać Customers
i używać tylko podwójnego rzutu z jednej z pozostałych odpowiedzi, ale zdaj sobie sprawę, że całkowicie omijasz bezpieczeństwo typów w czasie kompilacji, które otrzymujesz od typów ogólnych w tym walizka).
Mówiąc ogólnie, zawsze dobrze jest wziąć pod uwagę najszersze możliwe ogólne granice, które byłyby dopuszczalne podczas pisania metody, podwójnie, jeśli ma być używana jako metoda biblioteczna. Jeśli masz zamiar czytać tylko z listy, użyj List<? extends T>
zamiast List<T>
, na przykład - daje to dzwoniącym znacznie większy zakres argumentów, które mogą przekazać, i oznacza, że jest mniej prawdopodobne, że napotkają możliwe do uniknięcia problemy podobne do tego, mam tutaj.