2011-10-18 25 views
31

gram z zlokalizowana oraz tablic, wydaje następujący kod kompiluje grzywny,Array of Generic listy

ArrayList<Key> a = new ArrayList<Key>(); 

Ale kompilator narzeka na ten jeden,

ArrayList<Key>[] a = new ArrayList<Key>[10]; 

Czytając wpis w stackoverflow , Rozumiem, że jest to spowodowane typem Erasure i mogę to naprawić, używając,

ArrayList<Key>[] a = (ArrayList<Key> []) new ArrayList[10]; 
lub listy lista

ArrayList<ArrayList<Key>> b = new ArrayList<ArrayList<Key>>(); 

Ale nie mogę zrozumieć przyczyny tej sceny. Szczególnie, dlaczego drugi jest nielegalny, biorąc pod uwagę, że pierwszy jest całkowicie OK. I dlaczego kompilator nie narzeka na listę list.

+3

http://stackoverflow.com/questions/217065/cannot-create-an-array-of-linkedlists-in-java – tcb

Odpowiedz

22

Nie możesz mieć tablicy, ponieważ tablica wymaga typu surowego. Typujesz go w drugiej instancji, co sprawia, że ​​pasuje do zdefiniowanego typu, i dlatego jest legalny (jednak jest to niemożliwe, aby mógł on wywnioskować). Lista list jest legalna, ponieważ ArrayList nie jest tablicą.

Przeczytaj rozdział 7.3 (strona 15) w official tutorial, aby uzyskać więcej informacji na ten temat.

typu części obiektu tablicy może być zmiennym lub typu parametryzowane, chyba że jest to (nieograniczony) wieloznaczne type.You możliwe rodzaje tablic DECLARE których rodzaj elementu jest zmiennym lub sparametryzowany typ, ale nie obiekty tablicowe. To jest denerwujące, na pewno. To ograniczenie jest konieczne, aby uniknąć takich sytuacji:

List<String>[] lsa = new List<String>[10]; // not really allowed 
Object o = lsa; 
Object[] oa = (Object[]) o; 
List<Integer> li = new ArrayList<Integer>(); 
li.add(new Integer(3)); 
oa[1] = li; // unsound, but passes run time store check 
String s = lsa[1].get(0); // run-time error - ClassCastException 

Jeśli tablice parametryzowanego typu były dozwolone, powyższy przykład byłby skompilować bez żadnych niekontrolowanych ostrzeżeń, a jednak nie w czasie wykonywania.

Tutorial potem idzie na powiedzieć, co następuje:

Ponieważ zmienne typu nie występują w czasie wykonywania, nie ma sposobu, aby ustalić, co rzeczywisty typ tablica będzie. Sposobem na obejście tego rodzaju ograniczeń literałów jest użycie klasy jako czas pracy typu tokenów

1

Macierze pozwalają uciec kontrole typu (jak pokazano w odpowiedzi na Chrisa). Tak więc możesz mieć kod, który przejdzie wszystkie sprawdzenia kompilatora (nie ma "niezatwierdzonych" ostrzeżeń z kompilatora), ale kończy się niepowodzeniem w czasie wykonywania z ClassCastException. Zabraniając tej konstrukcji, problem dotyczy programisty, dlatego pojawiają się ostrzeżenia.

4

Ja sam miałem numer similar question - FWIW, nie znalazłem odpowiedzi przekonujących. Stosowny odcinek od najbardziej szczegółową odpowiedź (w odniesieniu do wartości zadanej pdf) to:

typu części obiektu tablicy może być zmiennym lub typu parametryzowane, chyba że jest to (nieograniczony) typ wieloznaczny.Możesz zadeklarować typy tablic, których typem jest zmienna typu lub typ sparametryzowany , ale nie obiektami tablicowymi. Jest to irytujące, być pewnym, że jest . To ograniczenie jest konieczne w celu uniknięcia sytuacji, jak

 List<String>[] lsa = new List<String>[10]; // not really allowed 
     Object o = lsa; 
     Object[] oa = (Object[]) o; 
     List<Integer> li = new ArrayList<Integer>(); 
     li.add(new Integer(3)); 
     oa[1] = li; // unsound, but passes run time store check 
     String s = lsa[1].get(0); // run-time error - ClassCastException 

tak dlatego mogę kot Lista [], aby Object [], a następnie wsadzić coś nieprawidłowe w Object [], a następnie zapoznać się niepoprawnie z odniesieniem listy, przez odlewany ref, to jest złe/niedozwolone? Ale tylko z nowymi?

Wciąż jest dla mnie bardziej niejasnym, jak deklarowanie tego za pomocą nowego jest mniej lub bardziej problematyczne niż użycie, ciągle wpatrując się w niego w nadziei, że zacznie to mieć sens, lub najmniej rozwiązać w ładny obraz 3d.

+0

kompilator musi gwarancja bezpieczeństwa rodzaju generycznego; kiedy nie może, musi albo nie zezwolić na kod, albo wydać ostrzeżenie. w tym przykładzie ostrzeżenie w drugiej linii wydaje się wystarczające; Decyzja javy, by jawnie zabronić generycznego tworzenia tablic, wydaje się zbyt trudna. – irreputable

+1

Tak 'ArrayList [] a = (ArrayList []) nowa ArrayList [10]; 'nadal ma ten sam problem, ale sprawia, że ​​jest wyraźniejsze, że oszukujesz system typu (i generuje on ostrzeżenie). – finnw

+0

finnw, twój komentarz jest uważany za właściwą odpowiedź i jedyny sposób, który ma dla mnie sens. –

2

Tworzenie ogólnych tablic nie jest bezpieczne (patrz "Pozycja 25: Preferuj listy do tablic" "Skuteczne Java - drugie wydanie" Joshua Blocha).

Zastosowanie:

List<List<Key>> b = new ArrayList<List<Key>>(10); 

Albo z Java SE 7:

List<List<Key>> b = new ArrayList<>(10); 
4

Array było rodzajowych biedaka; w przypadku prawdziwych leków generycznych należy unikać tablic, choć nie zawsze jest to możliwe.

Tablice są kowariantne, generyczne są niezmienne; w połączeniu z kasowaniem rzeczy nie pasują do siebie bardzo dobrze, co ilustruje przykład w odpowiedzi Chrisa.

Jednak myślę, że można rozluźnić specyfikację, aby umożliwić generyczne tworzenie macierzy - naprawdę nie ma problemu. Niebezpieczeństwo pojawia się podczas rzucania tablicy; ostrzeżenie kompilatora w tym momencie jest wystarczające.

Właściwie Java tworzy ogólne tablice dla metod vararg, więc jest to trochę obłudne.

Oto metody narzędziowe Korzystając z tego faktu

@SafeVarargs 
static <E> E[] arrayLiteral(E... array) 
{ 
    return array; 
} 

@SafeVarargs 
static <E> E[] newArray(int length, E... array) 
{ 
    return Arrays.copyOf(array, length); 
} 

// usage 

    List<String>[] array1 = arrayLiteral(list, list); 

    List<String>[] array2 = newArray(10); 
+0

Jest to prawdopodobnie spowodowane kompatybilnością wsteczną. Załóżmy, że przyszła wersja (Java 9?) Pozwala na generyczne tworzenie macierzy, a gdy kod Java 9 zostanie połączony z kodem Java 5/6/7/8 (który może wysyłać tablice bez ostrzeżenia), wynikiem będzie nieoczekiwany ClassCastExceptions. – finnw

+0

To nie działa, jeśli 'E' jest zmienną typu. Plik varargs tworzy tablicę wymazania 'E', gdy' E' jest zmienną typu, przez co nie różni się zbytnio od '(E []) new Object [n]'. Zobacz [http://ideone.com/T8xF91](http://ideone.com/T8xF91). – Radiodef

Powiązane problemy