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.
To też potrzebuje @SuppressWarnings("unchecked"). Zauważ, że możesz także przesyłać do (List)zamiast do (Object).
200_success
36
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.
Nie, nie jest. Ominąłbyś bezpieczeństwo typu, gdyby powyższe było dozwolone. Zauważ, że przesyłanie nie oznacza tworzenia nowej listy i kopiowania elementów. Oznacza to obsługę pojedynczej instancji jako innego typu, a zatem masz listę zawierającą potencjalnie obiekty inne niż Customer z gwarancją bezpieczeństwa typu, której nie powinien. Nie ma to nic wspólnego z java jako taką. Ponieważ czujesz, że ta funkcja sprawia, że Java utknęła w mrocznych czasach, wzywam Cię do wyjaśnienia, jak Twoim zdaniem powinno to działać.
Lasse V. Karlsen
1
@ BrainSlugs83 dla twoich potrzeb (List <Customer>) (Object) list;
Muthu Ganapathy Nathan
@ LasseV.Karlsen Nie mówię o castingu, mówię o konwersji. Nie obejdzie bezpieczeństwa typu, wymusi to. Implementacja typów ogólnych w Javie, brak przeciążeń operatorów, metod rozszerzających i wielu innych nowoczesnych udogodnień sprawiły, że jestem niesamowicie rozczarowany. - W międzyczasie .NET ma do tego dwie oddzielne metody rozszerzeń - jedną wywołaną .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).
BrainSlugs83
1
@EAGER_STUDENT Nie pomijałbym Javy, która może faktycznie działać (cała ta absurdalna firma wymazywania typów, musiałbym spróbować ...) - ale nie, nigdy nie napisałbym takiego kodu - przegrywasz bezpieczeństwo typów, co się stanie, jeśli jeden element kolekcji nie jest instanceofKlientem?
BrainSlugs83
1
@ BrainSlugs83 Osoba, która zadała pytanie, zapytała konkretnie o casting. A jeśli Java nie ma jeszcze odpowiednich metod, takich jak metody .NET, do których się odwołujesz (które nadal nie konwertują btw), powinieneś być w stanie je łatwo dodać. To wszystko jest jednak trochę ortogonalne w stosunku do omawianego pytania, które dotyczyło konkretnej składni.
Lasse V. Karlsen
35
W zależności od innego kodu najlepsza odpowiedź może się różnić. Próbować:
List<?extendsObject> 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.
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.
Przyszłością Javy jest ... Scala. Poważnie, facet, który umieścił typy generyczne w Javie, opracował nowy język, który jest nieco podobny do Javy, ale naprawdę bardzo biegły w typach: bardzo dokładna i spójna implementacja obsługi typów. Myślę, że nikt nie wie na pewno, które funkcje Scali powrócą do Javy i kiedy.
Carl Smotricz
doskonałe wyjaśnienie kowariancji, które naprawdę odpowiada na pytanie dotyczące PO. Dobra robota.
Dzień Kevina
Carl: Myślałem, że niektórzy programiści Java zaczęli tworzyć C #? :) Zresztą tak, Java najprawdopodobniej zmierza w przyszłości w kierunku Scali zamiast, powiedzmy, czegoś mniej silnie wpisanego.
Esko
@Carl - w Scali istnieje subtelna różnica w tym, że domyślnie listy są niezmienne. Więc generalnie nie masz problemu z dodaniem obiektu do listy klientów, ponieważ kiedy to zrobisz, otrzymasz nową listę obiektów .
Brian Agnew
Eee ... technicznie jest to poprawne - ale nawet przed .NET 4.0 - można to zrobić za pomocą zwykłych metod rozszerzeń IEnumerable (.Cast <> i .OfType <>) - więc nie ma potrzeby schodzenia z głębokiego końca, jeśli chcesz po prostu silnej iteracji typu.
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 Objectna a Customeri 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:
returnLists.transform(list,newFunction<Object,Customer>(){publicCustomer apply(Object from){if(from instanceofCustomer){return(Customer)from;}returnnull;// or throw an exception, or do something else that makes sense.}});
Podoba mi się to rozwiązanie. Nawet jeśli musisz dodać SuppressWarnings, lepiej jest dodać go w jednym miejscu niż przy każdym niebezpiecznym rzucaniu.
robson
1
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ć Customerobiekty do listy, możesz zadeklarować to w następujący sposób:
...List<Object> list = getList();return(List<?superCustomer>) 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ć Customersi 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.
Odpowiedzi:
zawsze możesz rzutować dowolny obiekt na dowolny typ, przesyłając go najpierw do Object. w Twoim przypadku:
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.
źródło
@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.
źródło
.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ć:
lub
Pamiętaj jednak, że nie zaleca się wykonywania takich niekontrolowanych rzutów.
źródło
Ze strumieniami Java 8 :
Czasami rzucanie siłą jest w porządku:
Ale oto bardziej wszechstronne rozwiązanie:
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:
źródło
FluentIterable
na mnie zadziałała.Możesz użyć podwójnej obsady.
źródło
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):
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.
źródło
Innym podejściem byłoby użycie strumienia Java 8.
źródło
Powinieneś po prostu powtórzyć listę i rzucić wszystkie Obiekty jeden po drugim
źródło
Możesz zrobić coś takiego
Lub sposób Java 8
źródło
Nie możesz, ponieważ
List<Object>
iList<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żdyObject
na aCustomer
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ącyList<Object>
zawiera coś, co nie jest plikiemCustomer
.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.
źródło
Możesz stworzyć nową Listę i dodać do niej elementy:
Na przykład:
źródło
Najlepszym rozwiązaniem jest utworzenie nowego
List<Customer>
, iteracyjne przeglądanieList<Object>
, dodanie każdej pozycji do nowej listy i zwrócenie jej.źródło
Jak zauważyli inni, nie możesz ich bezpiecznie rzucać, ponieważ a
List<Object>
nie jestList<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:źródło
Podobnie z Bozho powyżej. Możesz obejść ten problem tutaj (chociaż mi się to nie podoba) za pomocą tej metody:
Tak. Spowoduje to rzutowanie listy na wymagany typ ogólny.
W podanym powyżej przypadku możesz wykonać taki kod:
źródło
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: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>
zamiastList<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.źródło