Typ odwołania, który jest tworzony przez przyjęcie nazwy ogólnej deklaracji typu bez towarzyszącej listy argumentów typu.
Typ tablicy, której typ elementu jest typem surowym.
Typ nienależący do statictypu surowego, Rktóry nie jest dziedziczony z nadklasy lub superinterfejsu R.
Oto przykład ilustrujący:
publicclassMyType<E>{classInner{}staticclassNested{}publicstaticvoid main(String[] args){MyType mt;// warning: MyType is a raw typeMyType.Inner inn;// warning: MyType.Inner is a raw typeMyType.Nested nest;// no warning: not parameterized typeMyType<Object> mt1;// no warning: type parameter givenMyType<?> mt2;// no warning: type parameter given (wildcard OK!)}}
Tutaj MyType<E>jest typ sparametryzowany ( JLS 4.5 ). Zwykle potocznie określa się ten typ jako po prostu MyTypew skrócie, ale technicznie nazwa to MyType<E>.
mtma typ raw (i generuje ostrzeżenie o kompilacji) na podstawie pierwszego punktu w powyższej definicji; innma również typ surowy według trzeciego punktu.
MyType.Nestednie jest sparametryzowanym typem, nawet jeśli jest to typ członka sparametryzowanego typu MyType<E>, ponieważ jest static.
mt1, i mt2oba są zadeklarowane za pomocą rzeczywistych parametrów typu, więc nie są to typy surowe.
Co jest takiego specjalnego w typach surowych?
Zasadniczo typy surowe zachowują się tak, jak przed wprowadzeniem leków generycznych. Oznacza to, że w czasie kompilacji poniższe informacje są całkowicie legalne.
List names =newArrayList();// warning: raw type!
names.add("John");
names.add("Mary");
names.add(Boolean.FALSE);// not a compilation error!
Powyższy kod działa dobrze, ale załóżmy, że masz również:
for(Object o : names){String name =(String) o;System.out.println(name);}// throws ClassCastException!// java.lang.Boolean cannot be cast to java.lang.String
Teraz napotykamy problemy w czasie wykonywania, ponieważ nameszawiera coś, co nie jest instanceof String.
Można przypuszczać, że jeśli chcesz, namesaby zawierał tylko String, to mógłby być może nadal korzystać typ surowca i ręcznie sprawdzić każdyadd samodzielnie, a następnie ręcznie odlewane do Stringkażdego elementu z names. Jeszcze lepiej , chociaż NIE należy używać surowego typu i pozwolić kompilatorowi wykonać całą pracę za Ciebie , wykorzystując moc generycznych programów Java.
Czym różni się typ raw od używania <Object>jako parametrów typu?
Oto cytat z Effective Java 2nd Edition, pozycja 23: Nie używaj surowych typów w nowym kodzie :
Jaka jest różnica między typem surowym Lista typem sparametryzowanym List<Object>? Mówiąc luźniej, ten pierwszy zrezygnował z ogólnego sprawdzania typów, podczas gdy ten drugi wyraźnie powiedział kompilatorowi, że jest w stanie pomieścić obiekty dowolnego typu. Chociaż można przekazać a List<String>do parametru typu List, nie można go przekazać do parametru typu List<Object>. Istnieją reguły podtypów dla rodzajów ogólnych i List<String>jest to podtyp typu surowego List, ale nie typu sparametryzowanego List<Object>. W rezultacie tracisz bezpieczeństwo typu, jeśli używasz typu surowego List, ale nie, jeśli używasz typu sparametryzowanegoList<Object> .
Aby zilustrować tę kwestię, rozważ następującą metodę, która bierze List<Object>a dodaje a new Object().
Gdybyś zadeklarował, appendNewObjectże Listjako parametr użyjesz surowego typu , to się skompiluje, a zatem stracisz bezpieczeństwo typu, które otrzymujesz od generycznych.
Czym różni się typ raw od używania <?>jako parametru typu?
List<Object>, List<String>itd. są wszystkie List<?>, więc kuszące może być stwierdzenie, że są po prostu List. Jest jednak zasadnicza różnica: ponieważ List<E>tylko definicja add(E), nie można dodać dowolnego obiektu do List<?>. Z drugiej strony, ponieważ typ surowy Listnie ma bezpieczeństwa typu, możesz zrobić addprawie wszystko List.
staticvoid appendNewObject(List<?> list){
list.add(newObject());// compilation error!}//...List<String> names =newArrayList<String>();
appendNewObject(names);// this part is fine!
Kompilator wykonał wspaniałą pracę, chroniąc cię przed potencjalnym naruszeniem niezmienności typu List<?>! Jeśli zadeklarujesz parametr jako typ surowy List list, kod skompiluje się i naruszysz niezmiennik typu List<String> names.
Surowy typ to kasowanie tego typu
Powrót do JLS 4.8:
Możliwe jest użycie jako typu skasowania sparametryzowanego typu lub skasowania typu tablicowego, którego typem elementu jest sparametryzowany typ. Taki typ nazywany jest typem surowym .
[...]
Nadklasy (odpowiednio superinterfaces) typu surowego są wymazaniami nadklas (superinterfaces) dowolnej parametryzacji typu ogólnego.
Typ konstruktora, metoda instancji lub niepola statictypu surowego, Cktóry nie jest dziedziczony z jego nadklas lub superinterfejsów, jest typem surowym, który odpowiada usunięciu jego typu w ogólnej deklaracji odpowiadającej C.
Mówiąc prościej, gdy używany jest typ surowy, konstruktory, metody instancji i niepole staticsą również usuwane .
Usuwanie typu mapuje również sygnaturę konstruktora lub metody na sygnaturę, która nie ma sparametryzowanych typów ani zmiennych typów. Kasowanie sygnatury konstruktora lub metody sjest sygnaturą składającą się z tej samej nazwy si kasowania wszystkich formalnych typów parametrów podanych w s.
Typ zwracany przez metodę i parametry typu ogólnej metody lub konstruktora również są usuwane, jeśli metoda lub podpis konstruktora zostaną usunięte.
Usunięcie podpisu metody ogólnej nie ma parametrów typu.
Poniższy raport o błędach zawiera przemyślenia Maurizio Cimadamore, twórcy kompilatora i Alexa Buckley'a, jednego z autorów JLS, na temat tego, dlaczego takie zachowanie powinno nastąpić: https://bugs.openjdk.java.net/browse / JDK-6400189 . (Krótko mówiąc, upraszcza to specyfikację).
Jeśli jest to niebezpieczne, dlaczego można używać surowego typu?
Oto kolejny cytat z JLS 4.8:
Użycie typów surowych jest dozwolone tylko jako ustępstwo do zgodności ze starszym kodem. Użycie typów surowych w kodzie napisanym po wprowadzeniu generyczności do języka programowania Java jest zdecydowanie odradzane. Możliwe, że przyszłe wersje języka programowania Java nie zezwalają na stosowanie typów surowych.
Efektywna Java 2. edycja ma również tę opcję:
Biorąc pod uwagę, że nie powinieneś używać typów surowych, dlaczego projektanci języków na to zezwolili? Aby zapewnić zgodność.
Platforma Java miała wkroczyć w drugą dekadę, kiedy wprowadzono generyczne, i istniała ogromna ilość kodu Java, który nie korzystał z generycznych. Uznano za krytyczne, że cały ten kod pozostaje legalny i interoperacyjny z nowym kodem, który używa generycznych. Musi być legalne przekazywanie wystąpień sparametryzowanych typów do metod zaprojektowanych do użycia ze zwykłymi typami i odwrotnie. Ten wymóg, znany jako kompatybilność migracji , podjął decyzję o wsparciu typów surowych.
Podsumowując, typy surowe NIGDY nie powinny być używane w nowym kodzie. Zawsze powinieneś używać sparametryzowanych typów .
Czy nie ma wyjątków?
Niestety, ponieważ generyczne Java nie są weryfikowane, istnieją dwa wyjątki, w których typy surowe muszą być użyte w nowym kodzie:
Literały klasowe, np. List.classNieList<String>.class
instanceofoperand, np. o instanceof Setnieo instanceof Set<String>
Co masz na myśli, mówiąc, że „generyczne elementy Java są niezrefikowane”?
Carl G,
7
W przypadku drugiego wyjątku składnia o instanceof Set<?>jest również dozwolona w celu uniknięcia typu surowego (choć w tym przypadku jest to tylko powierzchowne).
Paul Bellora,
1
Typy surowe są bardzo przydatne i zmniejszają kod bojlera w przypadku wyszukiwania JNDI dla komponentu bean, który rozszerza interfejs. To rozwiązuje potrzebę pisania nzdalnych komponentów bean dla każdej klasy implementującej z identycznym kodem.
djmj
8
„Niezrefikowany” to kolejny sposób na stwierdzenie, że zostały usunięte. Kompilator wie, jakie są parametry ogólne, ale informacje te nie są przekazywane do wygenerowanego kodu bajtowego. JLS wymaga, aby literały klas nie miały parametrów typu.
Erick G. Hagstrom,
2
@OldCurmudgeon To ciekawe. Mam na myśli, że oficjalnie tak nie jest , ponieważ literał klasy zdefiniowany jako just TypeName.class, gdzie TypeNamejest zwykły identyfikator ( jls ). Mówiąc hipotetycznie, myślę, że tak może być. Być może jako wskazówka List<String>.classjest wariant, który JLS nazywa konkretnie błędem kompilatora, więc jeśli kiedykolwiek dodadzą go do języka, spodziewam się, że to ten, którego używają.
Radiodef
62
Jakie są typy raw w Javie i dlaczego często słyszę, że nie powinny być używane w nowym kodzie?
Surowe typy to starożytna historia języka Java. Na początku były Collectionsi nie posiadały Objectsnic więcej i nic mniej. Każda operacja na Collectionswymaganych rzutach od Objectdo pożądanego typu.
List aList =newArrayList();String s ="Hello World!";
aList.add(s);String c =(String)aList.get(0);
Chociaż działało to przez większość czasu, błędy się zdarzały
List aNumberList =newArrayList();String one ="1";//Number one
aNumberList.add(one);Integer iOne =(Integer)aNumberList.get(0);//Insert ClassCastException here
Stare beztypowe kolekcje nie mogły wymuszać bezpieczeństwa typu, więc programista musiał pamiętać, co przechował w kolekcji.
Gdy opracowano ogólne, aby obejść to ograniczenie, programista zadeklaruje typ przechowywany jeden raz, a kompilator zrobi to zamiast tego.
List<String> aNumberList =newArrayList<String>();
aNumberList.add("one");Integer iOne = aNumberList.get(0);//Compile time errorString sOne = aNumberList.get(0);//works fine
Dla porownania:
// Old style collections now known as raw typesList aList =newArrayList();//Could contain anything// New style collections with GenericsList<String> aList =newArrayList<String>();//Contains only Strings
Bardziej złożony interfejs porównywalny:
//raw, not type save can compare with Other classesclassMyCompareAbleimplementsCompareAble{int id;publicint compareTo(Object other){returnthis.id -((MyCompareAble)other).id;}}//GenericclassMyCompareAbleimplementsCompareAble<MyCompareAble>{int id;publicint compareTo(MyCompareAble other){returnthis.id - other.id;}}
Zauważ, że nie można zaimplementować CompareAbleinterfejsu z compareTo(MyCompareAble)typami raw. Dlaczego nie powinieneś ich używać:
Wszelkie Objectprzechowywane w a Collectionmuszą zostać oddane przed użyciem
Korzystanie ze składników ogólnych umożliwia sprawdzanie czasu kompilacji
Używanie typów raw jest takie samo jak przechowywanie każdej wartości jako Object
Co robi kompilator: Generics są wstecznie kompatybilne, używają tych samych klas java co surowe typy. Magia dzieje się głównie w czasie kompilacji.
List<String> someStrings =newArrayList<String>();
someStrings.add("one");String one = someStrings.get(0);
Zostanie skompilowany jako:
List someStrings =newArrayList();
someStrings.add("one");String one =(String)someStrings.get(0);
Jest to ten sam kod, który napisałbyś, gdybyś używał typów surowych bezpośrednio. Pomyślałem, że nie jestem pewien, co dzieje się z CompareAbleinterfejsem, myślę, że tworzy on dwie compareTofunkcje, jedna bierze MyCompareAblea druga bierze Objecti przekazuje do pierwszej po rzuceniu.
Jakie są alternatywy dla typów surowych: Użyj generycznych
Aby utworzyć sparametryzowany typ Box<T>, podajesz argument typu rzeczywistego dla formalnego parametru typu T:
Box<Integer> intBox =newBox<>();
Jeśli argument typu rzeczywistego zostanie pominięty, tworzony jest surowy typ Box<T>:
Box rawBox =newBox();
Dlatego Boxjest typem surowym typu ogólnego Box<T>. Jednak nie ogólna klasa lub typ interfejsu nie jest typem surowym.
Typy surowe pojawiają się w starszym kodzie, ponieważ wiele klas API (takich jak klasy Kolekcje) nie było ogólnych przed JDK 5.0. Używając typów surowych, zasadniczo uzyskujesz zachowanie przedgeneryczne - a Boxdaje ci Objects. W celu zachowania zgodności wstecznej dozwolone jest przypisywanie sparametryzowanego typu do jego typu surowego:
Box<String> stringBox =newBox<>();Box rawBox = stringBox;// OK
Ale jeśli przypiszesz typ surowy do sparametryzowanego typu, pojawi się ostrzeżenie:
Box rawBox =newBox();// rawBox is a raw type of Box<T>Box<Integer> intBox = rawBox;// warning: unchecked conversion
Pojawia się również ostrzeżenie, jeśli używasz typu raw do wywoływania metod ogólnych zdefiniowanych w odpowiednim typie ogólnym:
Ostrzeżenie pokazuje, że typy surowe omijają ogólne sprawdzanie typów, odraczając przechwytywanie niebezpiecznego kodu do środowiska wykonawczego. Dlatego należy unikać używania typów surowych.
Sekcja Usuwanie typu zawiera więcej informacji o tym, w jaki sposób kompilator Java wykorzystuje typy surowe.
Niesprawdzone komunikaty o błędach
Jak wspomniano wcześniej, podczas mieszania starszego kodu z kodem ogólnym mogą pojawić się komunikaty ostrzegawcze podobne do następujących:
Uwaga: Example.java używa niesprawdzonych lub niebezpiecznych operacji.
Uwaga: Ponowna kompilacja z opcją -Xlint: niezaznaczone dla szczegółów.
Może się to zdarzyć przy użyciu starszego interfejsu API działającego na typach raw, jak pokazano w poniższym przykładzie:
publicclassWarningDemo{publicstaticvoid main(String[] args){Box<Integer> bi;
bi = createBox();}staticBox createBox(){returnnewBox();}}
Termin „niezaznaczony” oznacza, że kompilator nie ma wystarczających informacji o typie, aby wykonać wszystkie kontrole typu niezbędne do zapewnienia bezpieczeństwa typu. Ostrzeżenie „niezaznaczone” jest domyślnie wyłączone, choć kompilator daje podpowiedź. Aby zobaczyć wszystkie „niezaznaczone” ostrzeżenia, ponownie skompiluj z opcją -Xlint: niezaznaczone.
Ponowna kompilacja poprzedniego przykładu z opcją -Xlint: niezaznaczone ujawnia następujące dodatkowe informacje:
WarningDemo.java:4: warning:[unchecked] unchecked conversion
found :Box
required:Box<java.lang.Integer>
bi = createBox();^1 warning
Aby całkowicie wyłączyć niesprawdzone ostrzeżenia, użyj flagi -Xlint: -unchecked. @SuppressWarnings("unchecked")Adnotacja tłumi niezaznaczone ostrzeżenia. Jeśli nie znasz @SuppressWarningsskładni, zobacz Adnotacje.
„Surowy” typ w Javie to klasa, która nie jest ogólna i zajmuje się „surowymi” obiektami, a nie bezpiecznymi dla typu parametrami typu ogólnego.
Na przykład przed udostępnieniem ogólnych składników Java należy użyć klasy kolekcji takiej jak ta:
LinkedList list =newLinkedList();
list.add(newMyObject());MyObject myObject =(MyObject)list.get(0);
Kiedy dodajesz swój obiekt do listy, nie ma znaczenia, jaki to jest typ obiektu, a kiedy pobierzesz go z listy, musisz jawnie rzutować go na oczekiwany typ.
Używając ogólnych, usuwasz czynnik „nieznany”, ponieważ musisz jawnie określić, jaki typ obiektów może znaleźć się na liście:
LinkedList<MyObject> list =newLinkedList<MyObject>();
list.add(newMyObject());MyObject myObject = list.get(0);
Zauważ, że w przypadku ogólnych nie musisz rzutować obiektu pochodzącego z wywołania get, kolekcja jest wstępnie zdefiniowana do pracy tylko z MyObject. Ten fakt jest głównym czynnikiem napędzającym leki generyczne. Zmienia źródło błędów w czasie wykonywania na coś, co można sprawdzić podczas kompilacji.
Mówiąc dokładniej, typ surowy jest tym, co otrzymuje się po prostu pomijając parametry typu dla typu ogólnego. Typy raw były zawsze tylko funkcją zgodności wstecznej i potencjalnie mogą zostać usunięte. Możesz uzyskać podobne zachowanie za pomocą? parametry wieloznaczne.
John Flatness,
@zerocrates: podobny, ale inny! Używanie ?nadal zapewnia bezpieczeństwo typu. Omówiłem to w swojej odpowiedzi.
polygenelrycyny
19
privatestaticList<String> list =newArrayList<String>();
Powinieneś określić parametr typu.
Ostrzeżenie informuje, że typy zdefiniowane do obsługi rodzajów ogólnych powinny zostać sparametryzowane, a nie przy użyciu ich surowej postaci.
Listjest zdefiniowana generycznych wsparcia: public class List<E>. Pozwala to na wiele bezpiecznych operacji, które są sprawdzane podczas kompilacji.
Teraz zastąpione wnioskami diamentowymi w Javie 7private static List<String> list = new ArrayList<>();
Ian Campbell
14
Co to jest typ raw i dlaczego często słyszę, że nie należy ich używać w nowym kodzie?
„Typ surowy” to użycie klasy ogólnej bez określania argumentów typu dla sparametryzowanych typów, np. Użycie Listzamiast List<String>. Kiedy w Javie wprowadzono leki generyczne, zaktualizowano kilka klas, aby korzystały z nich. Użycie tych klas jako „typu surowego” (bez podania argumentu typu) pozwoliło na kompilację starszego kodu.
„Typy surowe” są używane dla kompatybilności wstecznej. Ich użycie w nowym kodzie nie jest zalecane, ponieważ użycie klasy ogólnej z argumentem typu pozwala na silniejsze pisanie, co z kolei może poprawić zrozumiałość kodu i prowadzić do wcześniejszego wychwytywania potencjalnych problemów.
Jaka jest alternatywa, jeśli nie możemy używać typów surowych i jak jest lepsza?
Preferowaną alternatywą jest użycie klas ogólnych zgodnie z przeznaczeniem - z argumentem odpowiedniego typu (np List<String>.). Pozwala to programiście na bardziej precyzyjne określenie typów, przekazuje przyszłym opiekunom więcej znaczenia na temat zamierzonego zastosowania zmiennej lub struktury danych, a także pozwala kompilatorowi wymusić lepsze bezpieczeństwo typu. Te zalety łącznie mogą poprawić jakość kodu i pomóc zapobiec wprowadzeniu niektórych błędów kodowania.
Na przykład dla metody, w której programista chce się upewnić, że zmienna List o nazwie „names” zawiera tylko ciągi znaków:
List<String> names =newArrayList<String>();
names.add("John");// OK
names.add(newInteger(1));// compile error
Ach, więc kusiło mnie, aby skopiować polygenelubricants„surowe” referencje z stackoverflow.com/questions/2770111/... do mojej własnej odpowiedzi, ale przypuszczam, że zostawię je do użycia we własnej odpowiedzi.
Bert F
1
tak, w zasadzie kopiowałem i wklejałem ten segment wszędzie, gdzie ludzie używają typów raw przy przepełnieniu stosu, i w końcu zdecydowałem się od razu odpowiedzieć na jedno pytanie. Mam nadzieję, że to dobry wkład dla społeczności.
polygenelrycyny
1
@polygenelubricants Zauważyłem - trafiliśmy na te same pytania :-)
Bert F
1
@ ha9u63ar: Rzeczywiście. Ogólnie rzecz biorąc, zwięzłe i proste odpowiedzi są co najmniej tak dobre, jak te długie i akceptowane.
displayName
Co to jest „mocniejsze odsysanie”?
carloswm85,
12
Kompilator chce, abyś napisał:
privatestaticList<String> list =newArrayList<String>();
ponieważ w przeciwnym razie możesz dodać dowolny typ list, co sprawia, że tworzenie instancji jest new ArrayList<String>()bezcelowe. Generyczne elementy języka Java są tylko funkcją czasu kompilacji, więc obiekt utworzony za pomocą new ArrayList<String>()tego elementu chętnie zaakceptuje elementy Integerlub JFrameelementy, jeśli zostaną przypisane do odwołania do „typu surowego” List- sam obiekt nie wie nic o typach, które powinien zawierać, tylko kompilator.
ArrayList<String> arrjest to ArrayListzmienna referencyjna z typem, Stringktóra odnosi się do ArralyListobiektu typu String. Oznacza to, że może przechowywać tylko obiekt typu String.
Nie jest Stringtypem surowym, więc nigdy nie wywoła ostrzeżenia.
arr.add("hello");// alone statement will compile successfully and no warning.
arr.add(23);//prone to compile time error.//error: no suitable method found for add(int)
Przypadek 2
W tym przypadku ArrayList<String> arrjest to typ ścisły, ale Twój obiekt new ArrayList();jest typem surowym.
arr.add("hello");//alone this compile but raise the warning.
arr.add(23);//again prone to compile time error.//error: no suitable method found for add(int)
tutaj arrjest typ ścisły. Tak więc podniesie błąd czasu kompilacji podczas dodawania integer.
Ostrzeżenie : - RawObiekt typu odwołuje się do Stricttypu Zmienna referencyjna typu ArrayList.
Przypadek 3
W tym przypadku ArrayList arrjest to typ surowy, ale twój obiekt new ArrayList<String>();jest typem ścisłym.
arr.add("hello");
arr.add(23);//compiles fine but raise the warning.
Doda do niego dowolny typ obiektu, ponieważ arrjest to typ surowy.
Ostrzeżenie : - StrictObiekt typu odwołuje się do rawtypu Zmienna.
Typ surowy to brak parametru typu, gdy używany jest typ ogólny.
Raw typu nie powinny być stosowane, ponieważ może to spowodować błędy wykonawcze, jak wstawiając doublew to, co miało być Setod ints.
Set set =newHashSet();
set.add(3.45);//ok
Podczas odzyskiwania rzeczy z Set, nie wiesz, co wychodzi. Załóżmy, że oczekujesz, że to będzie wszystko int, do czego rzucasz Integer; wyjątek w czasie wykonywania, gdy doublepojawi się wersja 3.45.
Po dodaniu do parametru parametru typuSet od razu pojawi się błąd kompilacji. Ten zapobiegawczy błąd pozwala naprawić problem, zanim coś wybuchnie podczas działania (oszczędzając w ten sposób czas i wysiłek).
Set<Integer> set =newHashSet<Integer>();
set.add(3.45);//NOT ok.
Jak wspomniano w zaakceptowanej odpowiedzi, tracisz wszelką obsługę generyków w kodzie surowego typu. Każdy parametr typu jest konwertowany na jego skasowanie (co w powyższym przykładzie jest po prostu Object).
Chodzi o to, że twój listjestList nieokreślonym przedmiotem. Oznacza to, że Java nie wie, jakie obiekty znajdują się na liście. Następnie, gdy chcesz iterować listę, musisz rzucić każdy element, aby mieć dostęp do właściwości tego elementu (w tym przypadku String).
Zasadniczo lepiej jest sparametryzować kolekcje, więc nie masz problemów z konwersją, będziesz mógł dodawać tylko elementy sparametryzowanego typu, a Twój edytor zaoferuje odpowiednie metody wyboru.
privatestaticList<String> list =newArrayList<String>();
Typy surowe odnoszą się do używania typu ogólnego bez określania parametru typu.
Na przykład ,
Lista jest typem surowym, a List<String>typem sparametryzowanym.
Kiedy w JDK 1.5 wprowadzono generics, surowe typy zostały zachowane tylko w celu zachowania kompatybilności wstecznej ze starszymi wersjami Javy. Mimo że używanie typów surowych jest nadal możliwe,
Należy ich unikać :
Zwykle wymagają rzutów
Nie są bezpieczne pod względem typu, a niektóre ważne rodzaje błędów pojawią się tylko w czasie wykonywania
Są mniej wyraziste i nie dokumentują się w taki sam sposób jak sparametryzowane typy
Przykład
import java.util.*;publicfinalclassAvoidRawTypes{void withRawType(){//Raw List doesn't self-document, //doesn't state explicitly what it can containList stars =Arrays.asList("Arcturus","Vega","Altair");Iterator iter = stars.iterator();while(iter.hasNext()){String star =(String) iter.next();//cast needed
log(star);}}void withParameterizedType(){List<String> stars =Arrays.asList("Spica","Regulus","Antares");for(String star: stars){
log(star);}}privatevoid log(Object message){System.out.println(Objects.toString(message));}}
Znalazłem tę stronę po kilku przykładowych ćwiczeniach i dokładnie takiej samej zagadce.
============== Poszedłem z tego kodu, jak zapewnia przykład ===============
publicstaticvoid main(String[] args)throwsIOException{Map wordMap =newHashMap();if(args.length >0){for(int i =0; i < args.length; i++){
countWord(wordMap, args[i]);}}else{
getWordFrequency(System.in, wordMap);}for(Iterator i = wordMap.entrySet().iterator(); i.hasNext();){Map.Entry entry =(Map.Entry) i.next();System.out.println(entry.getKey()+" :\t"+ entry.getValue());}
====================== Do tego kodu ========================
publicstaticvoid main(String[] args)throwsIOException{// replace with TreeMap to get them sorted by nameMap<String,Integer> wordMap =newHashMap<String,Integer>();if(args.length >0){for(int i =0; i < args.length; i++){
countWord(wordMap, args[i]);}}else{
getWordFrequency(System.in, wordMap);}for(Iterator<Entry<String,Integer>> i = wordMap.entrySet().iterator(); i.hasNext();){Entry<String,Integer> entry = i.next();System.out.println(entry.getKey()+" :\t"+ entry.getValue());}}
Odpowiedzi:
Co to jest typ surowy?
Specyfikacja języka Java definiuje typ surowy w następujący sposób:
JLS 4.8 Rodzaje surowców
Oto przykład ilustrujący:
Tutaj
MyType<E>
jest typ sparametryzowany ( JLS 4.5 ). Zwykle potocznie określa się ten typ jako po prostuMyType
w skrócie, ale technicznie nazwa toMyType<E>
.mt
ma typ raw (i generuje ostrzeżenie o kompilacji) na podstawie pierwszego punktu w powyższej definicji;inn
ma również typ surowy według trzeciego punktu.MyType.Nested
nie jest sparametryzowanym typem, nawet jeśli jest to typ członka sparametryzowanego typuMyType<E>
, ponieważ jeststatic
.mt1
, imt2
oba są zadeklarowane za pomocą rzeczywistych parametrów typu, więc nie są to typy surowe.Co jest takiego specjalnego w typach surowych?
Zasadniczo typy surowe zachowują się tak, jak przed wprowadzeniem leków generycznych. Oznacza to, że w czasie kompilacji poniższe informacje są całkowicie legalne.
Powyższy kod działa dobrze, ale załóżmy, że masz również:
Teraz napotykamy problemy w czasie wykonywania, ponieważ
names
zawiera coś, co nie jestinstanceof String
.Można przypuszczać, że jeśli chcesz,
names
aby zawierał tylkoString
, to mógłby być może nadal korzystać typ surowca i ręcznie sprawdzić każdyadd
samodzielnie, a następnie ręcznie odlewane doString
każdego elementu znames
. Jeszcze lepiej , chociaż NIE należy używać surowego typu i pozwolić kompilatorowi wykonać całą pracę za Ciebie , wykorzystując moc generycznych programów Java.Oczywiście, jeśli NIE chcesz
names
, aby umożliwićBoolean
, po czym można je zadeklarować jakList<Object> names
i powyższy kod będzie kompilować.Zobacz też
Czym różni się typ raw od używania
<Object>
jako parametrów typu?Oto cytat z Effective Java 2nd Edition, pozycja 23: Nie używaj surowych typów w nowym kodzie :
Aby zilustrować tę kwestię, rozważ następującą metodę, która bierze
List<Object>
a dodaje anew Object()
.Ogólne w Javie są niezmienne. A
List<String>
nie jest aList<Object>
, więc poniższe wygeneruje ostrzeżenie kompilatora:Gdybyś zadeklarował,
appendNewObject
żeList
jako parametr użyjesz surowego typu , to się skompiluje, a zatem stracisz bezpieczeństwo typu, które otrzymujesz od generycznych.Zobacz też
<E extends Number>
i<Number>
?Czym różni się typ raw od używania
<?>
jako parametru typu?List<Object>
,List<String>
itd. są wszystkieList<?>
, więc kuszące może być stwierdzenie, że są po prostuList
. Jest jednak zasadnicza różnica: ponieważList<E>
tylko definicjaadd(E)
, nie można dodać dowolnego obiektu doList<?>
. Z drugiej strony, ponieważ typ surowyList
nie ma bezpieczeństwa typu, możesz zrobićadd
prawie wszystkoList
.Rozważ następującą odmianę poprzedniego fragmentu:
Kompilator wykonał wspaniałą pracę, chroniąc cię przed potencjalnym naruszeniem niezmienności typu
List<?>
! Jeśli zadeklarujesz parametr jako typ surowyList list
, kod skompiluje się i naruszysz niezmiennik typuList<String> names
.Surowy typ to kasowanie tego typu
Powrót do JLS 4.8:
Mówiąc prościej, gdy używany jest typ surowy, konstruktory, metody instancji i niepole
static
są również usuwane .Weź następujący przykład:
Kiedy używamy surowego
MyType
,getNames
zostaje również wymazany, dzięki czemu zwraca suroweList
!JLS 4.6 nadal wyjaśnia, co następuje:
Poniższy raport o błędach zawiera przemyślenia Maurizio Cimadamore, twórcy kompilatora i Alexa Buckley'a, jednego z autorów JLS, na temat tego, dlaczego takie zachowanie powinno nastąpić: https://bugs.openjdk.java.net/browse / JDK-6400189 . (Krótko mówiąc, upraszcza to specyfikację).
Jeśli jest to niebezpieczne, dlaczego można używać surowego typu?
Oto kolejny cytat z JLS 4.8:
Efektywna Java 2. edycja ma również tę opcję:
Podsumowując, typy surowe NIGDY nie powinny być używane w nowym kodzie. Zawsze powinieneś używać sparametryzowanych typów .
Czy nie ma wyjątków?
Niestety, ponieważ generyczne Java nie są weryfikowane, istnieją dwa wyjątki, w których typy surowe muszą być użyte w nowym kodzie:
List.class
NieList<String>.class
instanceof
operand, np.o instanceof Set
nieo instanceof Set<String>
Zobacz też
Collection<String>.class
nielegalny?źródło
o instanceof Set<?>
jest również dozwolona w celu uniknięcia typu surowego (choć w tym przypadku jest to tylko powierzchowne).n
zdalnych komponentów bean dla każdej klasy implementującej z identycznym kodem.TypeName.class
, gdzieTypeName
jest zwykły identyfikator ( jls ). Mówiąc hipotetycznie, myślę, że tak może być. Być może jako wskazówkaList<String>.class
jest wariant, który JLS nazywa konkretnie błędem kompilatora, więc jeśli kiedykolwiek dodadzą go do języka, spodziewam się, że to ten, którego używają.Surowe typy to starożytna historia języka Java. Na początku były
Collections
i nie posiadałyObjects
nic więcej i nic mniej. Każda operacja naCollections
wymaganych rzutach odObject
do pożądanego typu.Chociaż działało to przez większość czasu, błędy się zdarzały
Stare beztypowe kolekcje nie mogły wymuszać bezpieczeństwa typu, więc programista musiał pamiętać, co przechował w kolekcji.
Gdy opracowano ogólne, aby obejść to ograniczenie, programista zadeklaruje typ przechowywany jeden raz, a kompilator zrobi to zamiast tego.
Dla porownania:
Bardziej złożony interfejs porównywalny:
Zauważ, że nie można zaimplementować
CompareAble
interfejsu zcompareTo(MyCompareAble)
typami raw. Dlaczego nie powinieneś ich używać:Object
przechowywane w aCollection
muszą zostać oddane przed użyciemObject
Co robi kompilator: Generics są wstecznie kompatybilne, używają tych samych klas java co surowe typy. Magia dzieje się głównie w czasie kompilacji.
Zostanie skompilowany jako:
Jest to ten sam kod, który napisałbyś, gdybyś używał typów surowych bezpośrednio. Pomyślałem, że nie jestem pewien, co dzieje się z
CompareAble
interfejsem, myślę, że tworzy on dwiecompareTo
funkcje, jedna bierzeMyCompareAble
a druga bierzeObject
i przekazuje do pierwszej po rzuceniu.Jakie są alternatywy dla typów surowych: Użyj generycznych
źródło
Typ surowy to nazwa ogólnej klasy lub interfejsu bez żadnych argumentów typu. Na przykład biorąc pod uwagę ogólną klasę Box:
Aby utworzyć sparametryzowany typ
Box<T>
, podajesz argument typu rzeczywistego dla formalnego parametru typuT
:Jeśli argument typu rzeczywistego zostanie pominięty, tworzony jest surowy typ
Box<T>
:Dlatego
Box
jest typem surowym typu ogólnegoBox<T>
. Jednak nie ogólna klasa lub typ interfejsu nie jest typem surowym.Typy surowe pojawiają się w starszym kodzie, ponieważ wiele klas API (takich jak klasy Kolekcje) nie było ogólnych przed JDK 5.0. Używając typów surowych, zasadniczo uzyskujesz zachowanie przedgeneryczne - a
Box
daje ciObject
s. W celu zachowania zgodności wstecznej dozwolone jest przypisywanie sparametryzowanego typu do jego typu surowego:Ale jeśli przypiszesz typ surowy do sparametryzowanego typu, pojawi się ostrzeżenie:
Pojawia się również ostrzeżenie, jeśli używasz typu raw do wywoływania metod ogólnych zdefiniowanych w odpowiednim typie ogólnym:
Ostrzeżenie pokazuje, że typy surowe omijają ogólne sprawdzanie typów, odraczając przechwytywanie niebezpiecznego kodu do środowiska wykonawczego. Dlatego należy unikać używania typów surowych.
Sekcja Usuwanie typu zawiera więcej informacji o tym, w jaki sposób kompilator Java wykorzystuje typy surowe.
Niesprawdzone komunikaty o błędach
Jak wspomniano wcześniej, podczas mieszania starszego kodu z kodem ogólnym mogą pojawić się komunikaty ostrzegawcze podobne do następujących:
Może się to zdarzyć przy użyciu starszego interfejsu API działającego na typach raw, jak pokazano w poniższym przykładzie:
Termin „niezaznaczony” oznacza, że kompilator nie ma wystarczających informacji o typie, aby wykonać wszystkie kontrole typu niezbędne do zapewnienia bezpieczeństwa typu. Ostrzeżenie „niezaznaczone” jest domyślnie wyłączone, choć kompilator daje podpowiedź. Aby zobaczyć wszystkie „niezaznaczone” ostrzeżenia, ponownie skompiluj z opcją -Xlint: niezaznaczone.
Ponowna kompilacja poprzedniego przykładu z opcją -Xlint: niezaznaczone ujawnia następujące dodatkowe informacje:
Aby całkowicie wyłączyć niesprawdzone ostrzeżenia, użyj flagi -Xlint: -unchecked.
@SuppressWarnings("unchecked")
Adnotacja tłumi niezaznaczone ostrzeżenia. Jeśli nie znasz@SuppressWarnings
składni, zobacz Adnotacje.Oryginalne źródło: Poradniki Java
źródło
„Surowy” typ w Javie to klasa, która nie jest ogólna i zajmuje się „surowymi” obiektami, a nie bezpiecznymi dla typu parametrami typu ogólnego.
Na przykład przed udostępnieniem ogólnych składników Java należy użyć klasy kolekcji takiej jak ta:
Kiedy dodajesz swój obiekt do listy, nie ma znaczenia, jaki to jest typ obiektu, a kiedy pobierzesz go z listy, musisz jawnie rzutować go na oczekiwany typ.
Używając ogólnych, usuwasz czynnik „nieznany”, ponieważ musisz jawnie określić, jaki typ obiektów może znaleźć się na liście:
Zauważ, że w przypadku ogólnych nie musisz rzutować obiektu pochodzącego z wywołania get, kolekcja jest wstępnie zdefiniowana do pracy tylko z MyObject. Ten fakt jest głównym czynnikiem napędzającym leki generyczne. Zmienia źródło błędów w czasie wykonywania na coś, co można sprawdzić podczas kompilacji.
źródło
?
nadal zapewnia bezpieczeństwo typu. Omówiłem to w swojej odpowiedzi.Powinieneś określić parametr typu.
Ostrzeżenie informuje, że typy zdefiniowane do obsługi rodzajów ogólnych powinny zostać sparametryzowane, a nie przy użyciu ich surowej postaci.
List
jest zdefiniowana generycznych wsparcia:public class List<E>
. Pozwala to na wiele bezpiecznych operacji, które są sprawdzane podczas kompilacji.źródło
private static List<String> list = new ArrayList<>();
Co to jest typ raw i dlaczego często słyszę, że nie należy ich używać w nowym kodzie?
„Typ surowy” to użycie klasy ogólnej bez określania argumentów typu dla sparametryzowanych typów, np. Użycie
List
zamiastList<String>
. Kiedy w Javie wprowadzono leki generyczne, zaktualizowano kilka klas, aby korzystały z nich. Użycie tych klas jako „typu surowego” (bez podania argumentu typu) pozwoliło na kompilację starszego kodu.„Typy surowe” są używane dla kompatybilności wstecznej. Ich użycie w nowym kodzie nie jest zalecane, ponieważ użycie klasy ogólnej z argumentem typu pozwala na silniejsze pisanie, co z kolei może poprawić zrozumiałość kodu i prowadzić do wcześniejszego wychwytywania potencjalnych problemów.
Jaka jest alternatywa, jeśli nie możemy używać typów surowych i jak jest lepsza?
Preferowaną alternatywą jest użycie klas ogólnych zgodnie z przeznaczeniem - z argumentem odpowiedniego typu (np
List<String>
.). Pozwala to programiście na bardziej precyzyjne określenie typów, przekazuje przyszłym opiekunom więcej znaczenia na temat zamierzonego zastosowania zmiennej lub struktury danych, a także pozwala kompilatorowi wymusić lepsze bezpieczeństwo typu. Te zalety łącznie mogą poprawić jakość kodu i pomóc zapobiec wprowadzeniu niektórych błędów kodowania.Na przykład dla metody, w której programista chce się upewnić, że zmienna List o nazwie „names” zawiera tylko ciągi znaków:
źródło
polygenelubricants
„surowe” referencje z stackoverflow.com/questions/2770111/... do mojej własnej odpowiedzi, ale przypuszczam, że zostawię je do użycia we własnej odpowiedzi.Kompilator chce, abyś napisał:
ponieważ w przeciwnym razie możesz dodać dowolny typ
list
, co sprawia, że tworzenie instancji jestnew ArrayList<String>()
bezcelowe. Generyczne elementy języka Java są tylko funkcją czasu kompilacji, więc obiekt utworzony za pomocąnew ArrayList<String>()
tego elementu chętnie zaakceptuje elementyInteger
lubJFrame
elementy, jeśli zostaną przypisane do odwołania do „typu surowego”List
- sam obiekt nie wie nic o typach, które powinien zawierać, tylko kompilator.źródło
Rozważam wiele przypadków, w których można wyjaśnić tę koncepcję
Przypadek 1
ArrayList<String> arr
jest toArrayList
zmienna referencyjna z typem,String
która odnosi się doArralyList
obiektu typuString
. Oznacza to, że może przechowywać tylko obiekt typu String.Nie jest
String
typem surowym, więc nigdy nie wywoła ostrzeżenia.Przypadek 2
W tym przypadku
ArrayList<String> arr
jest to typ ścisły, ale Twój obiektnew ArrayList();
jest typem surowym.tutaj
arr
jest typ ścisły. Tak więc podniesie błąd czasu kompilacji podczas dodawaniainteger
.Przypadek 3
W tym przypadku
ArrayList arr
jest to typ surowy, ale twój obiektnew ArrayList<String>();
jest typem ścisłym.Doda do niego dowolny typ obiektu, ponieważ
arr
jest to typ surowy.źródło
Typ surowy to brak parametru typu, gdy używany jest typ ogólny.
Raw typu nie powinny być stosowane, ponieważ może to spowodować błędy wykonawcze, jak wstawiając
double
w to, co miało byćSet
odint
s.Podczas odzyskiwania rzeczy z
Set
, nie wiesz, co wychodzi. Załóżmy, że oczekujesz, że to będzie wszystkoint
, do czego rzucaszInteger
; wyjątek w czasie wykonywania, gdydouble
pojawi się wersja 3.45.Po dodaniu do parametru parametru typu
Set
od razu pojawi się błąd kompilacji. Ten zapobiegawczy błąd pozwala naprawić problem, zanim coś wybuchnie podczas działania (oszczędzając w ten sposób czas i wysiłek).źródło
Oto kolejny przypadek, w którym gryzą cię surowe typy:
Jak wspomniano w zaakceptowanej odpowiedzi, tracisz wszelką obsługę generyków w kodzie surowego typu. Każdy parametr typu jest konwertowany na jego skasowanie (co w powyższym przykładzie jest po prostu
Object
).źródło
Chodzi o to, że twój
list
jestList
nieokreślonym przedmiotem. Oznacza to, że Java nie wie, jakie obiekty znajdują się na liście. Następnie, gdy chcesz iterować listę, musisz rzucić każdy element, aby mieć dostęp do właściwości tego elementu (w tym przypadku String).Zasadniczo lepiej jest sparametryzować kolekcje, więc nie masz problemów z konwersją, będziesz mógł dodawać tylko elementy sparametryzowanego typu, a Twój edytor zaoferuje odpowiednie metody wyboru.
źródło
strona samouczka .
Typ surowy to nazwa ogólnej klasy lub interfejsu bez żadnych argumentów typu. Na przykład biorąc pod uwagę ogólną klasę Box:
Aby utworzyć sparametryzowany typ pola, podajesz argument typu rzeczywistego dla formalnego parametru typu T:
Jeśli argument typu rzeczywistego zostanie pominięty, tworzony jest surowy typ pola:
źródło
Unikaj surowych typów
Na przykład ,
Lista jest typem surowym, a
List<String>
typem sparametryzowanym.Kiedy w JDK 1.5 wprowadzono generics, surowe typy zostały zachowane tylko w celu zachowania kompatybilności wstecznej ze starszymi wersjami Javy. Mimo że używanie typów surowych jest nadal możliwe,
Należy ich unikać :
Są mniej wyraziste i nie dokumentują się w taki sam sposób jak sparametryzowane typy Przykład
W celach informacyjnych : https://docs.oracle.com/javase/tutorial/java/generics/rawTypes.html
źródło
Znalazłem tę stronę po kilku przykładowych ćwiczeniach i dokładnie takiej samej zagadce.
============== Poszedłem z tego kodu, jak zapewnia przykład ===============
====================== Do tego kodu ========================
================================================== =============================
Może być bezpieczniej, ale demontaż filozofii zajął 4 godziny ...
źródło
Surowe typy są w porządku, gdy wyrażają to, co chcesz wyrazić.
Na przykład funkcja deserializacji może zwrócić a
List
, ale nie zna typu elementu listy. TakList
jest tutaj odpowiedni typ zwrotu.źródło