Z jakiego powodu nie mogę tworzyć ogólnych typów tablic w Javie?

273

Jaki jest powód, dla którego Java nam na to nie pozwala

private T[] elements = new T[initialCapacity];

Zrozumiałem, że .NET nie pozwala nam na to, ponieważ w .NET masz typy wartości, które w czasie wykonywania mogą mieć różne rozmiary, ale w Javie wszystkie rodzaje T będą odwołaniami do obiektów, dzięki czemu mają ten sam rozmiar ( Popraw mnie, jeśli się mylę).

Jaki jest powód?

pochłonęło elysium
źródło
29
O czym mówisz? Możesz to absolutnie zrobić w .NET. - Próbuję dowiedzieć się, dlaczego nie mogę tego zrobić w Javie.
BrainSlugs83
@ BrainSlugs83 - dodaj link do przykładu kodu lub samouczka, który to pokazuje.
MasterJoe2
Zobacz także - stackoverflow.com/questions/21577493/…
MasterJoe2
1
@ MasterJoe2 powyższy kod w pytaniu PO jest tym, o czym mówię. Działa dobrze w języku C #, ale nie w Javie. - Pytanie stwierdza, że ​​w żadnym z nich nie działa, co jest nieprawidłowe. - Nie jestem pewien, czy warto dyskutować o tym dalej.
BrainSlugs83

Odpowiedzi:

204

Jest tak, ponieważ tablice Javy (w przeciwieństwie do ogólnych) zawierają w czasie wykonywania informacje o typie swojego komponentu. Musisz więc znać typ komponentu podczas tworzenia tablicy. Ponieważ nie wiesz, co Tjest w czasie wykonywania, nie możesz utworzyć tablicy.

newacct
źródło
29
Ale co z usunięciem? Dlaczego to nie dotyczy?
Qix - MONICA MISTREATED
14
Jak ArrayList <SomeType>to się wtedy robi ?
Thumbz
10
@Thumbz: Masz na myśli new ArrayList<SomeType>()? Typy ogólne nie zawierają parametru type w czasie wykonywania. Parametr typu nie jest używany podczas tworzenia. Nie ma różnicy w kodzie generowanym przez new ArrayList<SomeType>()lub new ArrayList<String>()czy new ArrayList()w ogóle.
newacct
8
Pytałem więcej o to, jak to ArrayList<T>działa ” private T[] myArray. Gdzieś w kodzie musi mieć tablicę ogólnego typu T, więc jak?
Thumbz
21
@Thumbz: Nie ma tablicy typu środowiska wykonawczego T[]. Ma tablicę typu środowiska wykonawczego Object[]i albo 1) kod źródłowy zawiera zmienną Object[](tak jest w najnowszym źródle Oracle Java); lub 2) kod źródłowy zawiera zmienną typu T[], która jest kłamstwem, ale nie powoduje problemów z powodu Tusunięcia z zakresu klasy.
newacct
137

Zacytować:

Tablice typów ogólnych są niedozwolone, ponieważ nie są prawidłowe. Problem jest spowodowany interakcją tablic Java, które nie są statycznie stabilne, ale są dynamicznie sprawdzane, z ogólnymi, które są statycznie stabilne i nie są dynamicznie sprawdzane. Oto jak możesz wykorzystać lukę:

class Box<T> {
    final T x;
    Box(T x) {
        this.x = x;
    }
}

class Loophole {
    public static void main(String[] args) {
        Box<String>[] bsa = new Box<String>[3];
        Object[] oa = bsa;
        oa[0] = new Box<Integer>(3); // error not caught by array store check
        String s = bsa[0].x; // BOOM!
    }
}

Zaproponowaliśmy rozwiązanie tego problemu za pomocą statycznie bezpiecznych tablic (alias Variance), które zostały odrzucone dla Tygrysa.

- ryflowanie

(Myślę, że to Neal Gafter , ale nie jestem pewien)

Zobacz to w kontekście tutaj: http://forums.sun.com/thread.jspa?threadID=457033&forumID=316

Bart Kiers
źródło
3
Zauważ, że zrobiłem to CW, ponieważ odpowiedź nie jest moja.
Bart Kiers
10
To wyjaśnia, dlaczego może nie być bezpieczny. Ale kompilator może ostrzegać o problemach związanych z bezpieczeństwem typu. Faktem jest, że nie jest to nawet możliwe, z prawie tego samego powodu, dla którego nie można tego zrobić new T(). Każda tablica w Javie z założenia przechowuje w niej typ komponentu (tj. T.class); dlatego potrzebujesz klasy T w czasie wykonywania, aby utworzyć taką tablicę.
newacct
2
Nadal możesz używać new Box<?>[n], co czasami może być wystarczające, chociaż nie pomogłoby to w twoim przykładzie.
Bartosz Klimek
1
@BartKiers Nie rozumiem ... to wciąż się nie kompiluje (java-8): Box<String>[] bsa = new Box<String>[3];czy coś się zmieniło w java-8 i więcej, zakładam?
Eugene
1
@Eugene, tablice określonych typów ogólnych po prostu nie są dozwolone, ponieważ mogą prowadzić do utraty bezpieczeństwa typu, jak wykazano w próbce. Jest to niedozwolone w żadnej wersji Java. Odpowiedź zaczyna się od: „Tablice typów ogólnych są niedozwolone, ponieważ nie są dźwiękiem”
granat
47

Nie dostarczając przyzwoitego rozwiązania, po prostu dostajesz coś gorszego IMHO.

Wspólne obejście jest następujące.

T[] ts = new T[n];

jest zastąpiony przez (zakładając, że T rozszerza Object, a nie inną klasę)

T[] ts = (T[]) new Object[n];

Wolę pierwszy przykład, jednak więcej typów akademickich wydaje się preferować drugi lub po prostu wolę o tym nie myśleć.

Większość przykładów, dlaczego nie możesz po prostu użyć Object [], odnosi się w równym stopniu do List lub Collection (które są obsługiwane), więc widzę je jako bardzo kiepskie argumenty.

Uwaga: jest to jeden z powodów, dla których sama biblioteka Kolekcje nie kompiluje się bez ostrzeżeń. Jeśli ten przypadek użycia nie może być obsługiwany bez ostrzeżeń, coś jest zasadniczo zepsute w ogólnym modelu IMHO.

Peter Lawrey
źródło
6
Musisz być ostrożny z drugim. Jeśli zwrócisz tablicę utworzoną w taki sposób komuś, kto oczekuje, powiedzmy, a String[](lub jeśli przechowujesz ją w polu publicznie dostępnym typu T[], a ktoś ją pobierze), wówczas otrzyma wyjątek ClassCastException.
newacct
4
Głosowałem za odrzuceniem tej odpowiedzi, ponieważ preferowany przykład nie jest dozwolony w Javie, a drugi przykład może zgłosić wyjątek ClassCast
José Roberto Araújo Júnior
5
@ JoséRobertoAraújoJúnior Jest całkiem jasne, że pierwszy przykład należy zastąpić drugim przykładem. Bardziej pomocne byłoby wyjaśnienie, dlaczego drugi przykład może zgłosić wyjątek ClassCastException, ponieważ nie byłoby to oczywiste dla wszystkich.
Peter Lawrey
3
@PeterLawrey Stworzyłem samo-odpowiedź na pytanie, dlaczego T[] ts = (T[]) new Object[n];jest to zły pomysł: stackoverflow.com/questions/21577493/...
José Roberto Araújo Júnior
1
@MarkoTopolnik Powinienem otrzymać medal za udzielenie odpowiedzi na wszystkie twoje komentarze, aby wyjaśnić to samo, co już powiedziałem, jedyną rzeczą, która zmieniła się z mojego pierwotnego powodu, jest to, że myślałem, że powiedział, że jest T[] ts = new T[n];to dobry przykład. Będę głosować, ponieważ jego odpowiedź może powodować problemy i zamieszanie w stosunku do innych deweloperów, a także jest nie na temat. Przestanę też komentować na ten temat.
José Roberto Araújo Júnior
38

Tablice są kowariantne

Mówi się, że tablice są kowariantne, co w zasadzie oznacza, że ​​biorąc pod uwagę reguły podtypów Java, tablica typu T[]może zawierać elementy typu Tlub dowolnego podtypu T. Na przykład

Number[] numbers = new Number[3];
numbers[0] = newInteger(10);
numbers[1] = newDouble(3.14);
numbers[2] = newByte(0);

Ale nie tylko to, reguły Java dla podtypów również stwierdzają, że tablica S[]jest podtypem tablicy, T[]jeśli Sjest podtypem T, dlatego też coś takiego jest również poprawne:

Integer[] myInts = {1,2,3,4};
Number[] myNumber = myInts;

Ponieważ zgodnie z regułami podtypów w Javie tablica Integer[]jest podtypem tablicy, Number[]ponieważ liczba całkowita jest podtypem liczby.

Ale ta zasada podsieci może prowadzić do interesującego pytania: co by się stało, gdybyśmy spróbowali to zrobić?

myNumber[0] = 3.14; //attempt of heap pollution

Ta ostatnia linia byłaby dobrze skompilowana, ale gdybyśmy uruchomili ten kod, otrzymalibyśmy, ArrayStoreExceptionponieważ próbujemy umieścić podwójny w tablicy liczb całkowitych. Fakt, że uzyskujemy dostęp do tablicy za pomocą odwołania do liczby, nie ma tutaj znaczenia, ważne jest to, że tablica jest tablicą liczb całkowitych.

Oznacza to, że możemy oszukać kompilator, ale nie możemy oszukać systemu typu wykonawczego. A to dlatego, że tablice są tym, co nazywamy typem możliwym do ponownego zdefiniowania. Oznacza to, że w czasie wykonywania Java wie, że ta tablica została faktycznie utworzona jako tablica liczb całkowitych, do których po prostu można uzyskać dostęp poprzez odwołanie typu Number[].

Tak więc, jak widzimy, jedna rzecz to faktyczny typ obiektu, a inna to rodzaj odwołania, którego używamy do uzyskania dostępu, prawda?

Problem z Generics Java

Problem z rodzajami rodzajowymi w Javie polega na tym, że kompilator porzuca kompilację informacji o typie dla parametrów typu; dlatego te informacje tego typu nie są dostępne w czasie wykonywania. Ten proces nazywa się kasowaniem typu . Istnieją dobre powody, aby implementować takie generyczne w Javie, ale to długa historia i ma ona związek z binarną zgodnością z istniejącym kodem.

Ważną kwestią jest to, że ponieważ w czasie wykonywania nie ma informacji o typie, nie ma sposobu, aby zapewnić, że nie popełniamy zanieczyszczenia hałdy.

Rozważmy teraz następujący niebezpieczny kod:

List<Integer> myInts = newArrayList<Integer>();
myInts.add(1);
myInts.add(2);
List<Number> myNums = myInts; //compiler error
myNums.add(3.14); //heap polution

Jeśli kompilator Java nie powstrzyma nas przed zrobieniem tego, system typów wykonawczych też nas nie powstrzyma, ponieważ w czasie wykonywania nie ma możliwości ustalenia, że ​​ta lista miała być tylko liczbą całkowitą. Środowisko wykonawcze Java pozwoliłoby nam umieścić na tej liście wszystko, co chcemy, gdy powinno ono zawierać tylko liczby całkowite, ponieważ kiedy zostało utworzone, zostało zadeklarowane jako lista liczb całkowitych. Dlatego kompilator odrzuca wiersz 4, ponieważ jest niebezpieczny i jeśli jest dozwolony, może złamać założenia systemu typów.

W związku z tym projektanci Java upewnili się, że nie możemy oszukać kompilatora. Jeśli nie możemy oszukać kompilatora (tak jak w przypadku tablic), nie możemy też oszukać systemu typu wykonawczego.

W związku z tym mówimy, że typy ogólne nie podlegają zwrotowi, ponieważ w czasie wykonywania nie możemy ustalić prawdziwej natury typu ogólnego.

Pominąłem niektóre części tych odpowiedzi, możesz przeczytać cały artykuł tutaj: https://dzone.com/articles/covariance-and-contravariance

Humoyun Ahmad
źródło
32

Powodem tego jest to, że Java implementuje swoje Generics wyłącznie na poziomie kompilatora, a dla każdej klasy generowany jest tylko jeden plik klasy. Nazywa się to Kasowaniem Typu .

W czasie wykonywania skompilowana klasa musi obsłużyć wszystkie swoje zastosowania przy użyciu tego samego kodu bajtowego. Tak więc new T[capacity]nie miałbym absolutnie pojęcia, jakiego typu należy utworzyć.

Durandal
źródło
17

Odpowiedź została już udzielona, ​​ale jeśli masz już wystąpienie T, możesz to zrobić:

T t; //Assuming you already have this object instantiated or given by parameter.
int length;
T[] ts = (T[]) Array.newInstance(t.getClass(), length);

Mam nadzieję, że mógłbym pomóc, Ferdi265

Ferdi265
źródło
1
To miłe rozwiązanie. Ale otrzymają niesprawdzone ostrzeżenia (rzutowane z Object na T []). Kolejny „wolniej”, ale rozwiązanie „ostrzeżenie-free” będzie: T[] ts = t.clone(); for (int i=0; i<ts.length; i++) ts[i] = null;.
midnite
1
Ponadto, jeśli to, co trzymaliśmy T[] t, to byłoby (T[]) Array.newInstance(t.getClass().getComponentType(), length);. spędziłem trochę czasu, żeby to rozgryźć getComponentType(). Mam nadzieję, że to pomaga innym.
midnite
1
@midnite t.clone()nie wróci T[]. Ponieważ tw tej odpowiedzi nie ma tablicy.
xmen
6

Głównym powodem jest to, że tablice w Javie są kowariantne.

Jest to dobry przegląd tutaj .

GaryF
źródło
Nie rozumiem, jak można obsługiwać „nowy T [5]” nawet przy niezmiennych tablicach.
Dimitris Andreou
2
@DimitrisAndreou Cóż, cała ta sprawa jest raczej komedią błędów w projekcie Java. Wszystko zaczęło się od kowariancji tablicowej. Wtedy, gdy masz tablicę kowariancji, można oddać String[]do Objecti przechowywać Integerw nim. Musieli więc dodać sprawdzanie typu środowiska wykonawczego dla sklepów tablicowych ( ArrayStoreException), ponieważ problem nie mógł zostać wykryty w czasie kompilacji. (W przeciwnym razie Integerrzeczywiście może utknąć w a String[], a przy próbie jego odzyskania wystąpiłby błąd, co byłoby okropne.) ...
Radon Rosborough
2
@DimitrisAndreou… Następnie, gdy umieścisz sprawdzanie czasu wykonywania w miejsce dalekosiężnej kontroli czasu kompilacji, natrafisz na kasowanie typu (również niefortunną wadę projektową - dołączoną tylko dla kompatybilności wstecznej). Usuwanie typu oznacza, że ​​nie można sprawdzać typów wykonawczych dla typów ogólnych. Dlatego, aby uniknąć problemu z typem przechowywania macierzy, po prostu nie możesz mieć ogólnych tablic. Gdyby po prostu uczyniły tablice niezmienniczymi, moglibyśmy po prostu sprawdzać typ kompilacji bez uciekania się do kasowania.
Radon Rosborough
… Właśnie odkryłem pięciominutowy okres edycji komentarzy. Objectpowinienem być Object[]w moim pierwszym komentarzu.
Radon Rosborough
3

Podoba mi się odpowiedź udzielona pośrednio przez Gaftera . Jednak proponuję, aby było źle. Trochę zmieniłem kod Gaftera. Kompiluje się i działa przez chwilę, a następnie bombarduje zgodnie z przewidywaniami Gaftera

class Box<T> {

    final T x;

    Box(T x) {
        this.x = x;
    }
}

class Loophole {

    public static <T> T[] array(final T... values) {
        return (values);
    }

    public static void main(String[] args) {

        Box<String> a = new Box("Hello");
        Box<String> b = new Box("World");
        Box<String> c = new Box("!!!!!!!!!!!");
        Box<String>[] bsa = array(a, b, c);
        System.out.println("I created an array of generics.");

        Object[] oa = bsa;
        oa[0] = new Box<Integer>(3);
        System.out.println("error not caught by array store check");

        try {
            String s = bsa[0].x;
        } catch (ClassCastException cause) {
            System.out.println("BOOM!");
            cause.printStackTrace();
        }
    }
}

Dane wyjściowe to

I created an array of generics.
error not caught by array store check
BOOM!
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
    at Loophole.main(Box.java:26)

Wydaje mi się, że możesz tworzyć ogólne typy tablic w Javie. Czy źle zrozumiałem pytanie?

emory
źródło
Twój przykład różni się od tego, o co prosiłem. Opisałeś zagrożenia związane z kowariancją macierzy. Sprawdź to (dla .NET: blogs.msdn.com/b/ericlippert/archive/2007/10/17/… )
pochłonęło elysium
Mam nadzieję, że otrzymasz od kompilatora ostrzeżenie dotyczące bezpieczeństwa typu, tak?
Matt McHenry
1
Tak, dostaję ostrzeżenie dotyczące bezpieczeństwa typu. Tak, widzę, że mój przykład nie odpowiada na pytanie.
emory
W rzeczywistości pojawia się wiele ostrzeżeń z powodu niechlujnej inicjalizacji a, b, c. Jest to również dobrze znane i wpływa na bibliotekę podstawową, np. <T> java.util.Arrays.asList (T ...). Jeśli przekażesz dowolny niepodlegający zwrotowi typ dla T, pojawi się ostrzeżenie (ponieważ utworzona tablica ma mniej dokładny typ niż udaje kod) i jest bardzo brzydka. Byłoby lepiej, gdyby autor tej metody otrzymał ostrzeżenie, zamiast emitować je w witrynie użytkowania, ponieważ sama metoda jest bezpieczna, nie ujawnia tablicy użytkownikowi.
Dimitris Andreou
1
Nie utworzono tutaj ogólnej tablicy. Kompilator utworzył dla ciebie tablicę (ogólną).
newacct
2

Wiem, że jestem trochę spóźniony na przyjęcie tutaj, ale pomyślałem, że mogę pomóc przyszłym pracownikom Google, ponieważ żadna z tych odpowiedzi nie rozwiązała mojego problemu. Jednak odpowiedź Ferdi265 bardzo pomogła.

Próbuję utworzyć własną listę połączoną, więc dla mnie zadziałał następujący kod:

package myList;
import java.lang.reflect.Array;

public class MyList<TYPE>  {

    private Node<TYPE> header = null;

    public void clear() {   header = null;  }

    public void add(TYPE t) {   header = new Node<TYPE>(t,header);    }

    public TYPE get(int position) {  return getNode(position).getObject();  }

    @SuppressWarnings("unchecked")
    public TYPE[] toArray() {       
        TYPE[] result = (TYPE[])Array.newInstance(header.getObject().getClass(),size());        
        for(int i=0 ; i<size() ; i++)   result[i] = get(i); 
        return result;
    }


    public int size(){
         int i = 0;   
         Node<TYPE> current = header;
         while(current != null) {   
           current = current.getNext();
           i++;
        }
        return i;
    }  

W metodzie toArray () leży sposób na stworzenie dla mnie tablicy typu ogólnego:

TYPE[] result = (TYPE[])Array.newInstance(header.getObject().getClass(),size());    
Derek Ziemba
źródło
2

W moim przypadku po prostu chciałem mieć szereg stosów, coś takiego:

Stack<SomeType>[] stacks = new Stack<SomeType>[2];

Ponieważ nie było to możliwe, zastosowałem następujące rozwiązanie:

  1. Utworzono nietypową klasę opakowania wokół stosu (powiedzmy MyStack)
  2. MyStack [] stacks = new MyStack [2] działał doskonale

Brzydkie, ale Java jest szczęśliwa.

Uwaga: jak wspomniano w BrainSlugs83 w komentarzu do pytania, jest całkowicie możliwe posiadanie tablic ogólnych w .NET

David Airapetyan
źródło
2

Z samouczka Oracle :

Nie można tworzyć tablic sparametryzowanych typów. Na przykład następujący kod się nie kompiluje:

List<Integer>[] arrayOfLists = new List<Integer>[2];  // compile-time error

Poniższy kod ilustruje, co się dzieje, gdy do tablicy wstawiane są różne typy:

Object[] strings = new String[2];
strings[0] = "hi";   // OK
strings[1] = 100;    // An ArrayStoreException is thrown.

Jeśli spróbujesz tego samego z ogólną listą, będzie problem:

Object[] stringLists = new List<String>[];  // compiler error, but pretend it's allowed
stringLists[0] = new ArrayList<String>();   // OK
stringLists[1] = new ArrayList<Integer>();  // An ArrayStoreException should be thrown,
                                            // but the runtime can't detect it.

Gdyby dozwolone były tablice sparametryzowanych list, poprzedni kod nie zgłosiłby żądanego wyjątku ArrayStoreException.

Dla mnie to brzmi bardzo słabo. Myślę, że każdy, kto ma wystarczającą wiedzę na temat generyków, byłby całkowicie w porządku, a nawet spodziewałby się, że wyjątek ArrayStoredException nie zostanie zgłoszony w takim przypadku.

Stick Hero
źródło
0

Na pewno musi być dobry sposób na obejście tego (może przy użyciu odbicia), ponieważ wydaje mi się, że właśnie to ArrayList.toArray(T[] a)działa. Cytuję:

public <T> T[] toArray(T[] a)

Zwraca tablicę zawierającą wszystkie elementy z tej listy we właściwej kolejności; typ środowiska uruchomieniowego zwróconej tablicy jest typem określonej tablicy. Jeśli lista mieści się w określonej tablicy, jest ona tam zwracana. W przeciwnym razie nowa tablica jest przydzielana z typem środowiska wykonawczego określonej tablicy i rozmiarem tej listy.

Tak więc jednym z rozwiązań byłoby użycie tej funkcji, tj. Utworzenie ArrayListobiektu, który chcesz w tablicy, a następnie użycietoArray(T[] a) do utworzenia rzeczywistej tablicy. Nie byłoby to szybkie, ale nie wspomniałeś o swoich wymaganiach.

Czy ktoś wie, jak toArray(T[] a)jest wdrażany?

Adam
źródło
3
List.toArray (T []) działa, ponieważ zasadniczo nadajesz mu typ komponentu T w czasie wykonywania (dajesz mu instancję żądanego typu tablicy, z której może uzyskać klasę tablicy, a następnie klasę komponentów T ). Przy rzeczywistym typie komponentu w środowisku wykonawczym zawsze możesz utworzyć tablicę tego typu środowiska wykonawczego za pomocą Array.newInstance(). Znajdziesz to wspomniane w wielu pytaniach, które pytają, jak utworzyć tablicę o typie nieznanym w czasie kompilacji. Ale OP specjalnie pytał, dlaczego nie można użyć new T[]składni, co jest innym pytaniem
newacct
0

To dlatego, że generycy zostali dodani do java po ich stworzeniu, więc jest to trochę niezgrabne, ponieważ oryginalni twórcy java myśleli, że podczas tworzenia tablicy typ będzie określony podczas jej tworzenia. Więc to nie działa z ogólnymi, więc musisz zrobić E [] array = (E []) new Object [15]; To się kompiluje, ale daje ostrzeżenie.

Alvin
źródło
0

Jeśli nie możemy utworzyć instancji ogólnych tablic, dlaczego język ma ogólne typy tablic? Po co mieć typ bez obiektów?

Jedynym powodem, dla którego mogę wymyślić, jest varargs - foo(T...). W przeciwnym razie mogliby całkowicie wyczyścić ogólne typy tablic. (Cóż, tak naprawdę nie musieli używać tablicy dla varargs, ponieważ varargs nie istniał przed 1.5 . To prawdopodobnie kolejny błąd).

Więc jest to kłamstwo, to może instancję tablic rodzajowe, poprzez varargs!

Oczywiście problemy z tablicami ogólnymi są nadal realne, np

static <T> T[] foo(T... args){
    return args;
}
static <T> T[] foo2(T a1, T a2){
    return foo(a1, a2);
}

public static void main(String[] args){
    String[] x2 = foo2("a", "b"); // heap pollution!
}

Możemy użyć tego przykładu, aby faktycznie wykazać niebezpieczeństwo związane z lekiem generycznym tablicą .

Z drugiej strony od dziesięciu lat używamy ogólnych varargów, a niebo jeszcze nie spada. Możemy więc argumentować, że problemy są przesadzone; to nic wielkiego. Jeśli dozwolone jest tworzenie jawnych tablic ogólnych, będziemy mieli błędy tu i tam; ale przyzwyczailiśmy się do problemów związanych z usuwaniem i możemy z tym żyć.

I możemy wskazać, aby foo2obalić twierdzenie, że specyfikacja chroni nas przed problemami, przed którymi twierdzą, że nas powstrzymują. Gdyby Sun miał więcej czasu i zasobów na 1,5 , uważam, że mogliby osiągnąć bardziej satysfakcjonującą rozdzielczość.

ZhongYu
źródło
0

Jak już wspomniano inni, możesz oczywiście tworzyć za pomocą kilku sztuczek .

Ale nie jest to zalecane.

Ponieważ typ wymazuje, a co ważniejsze, covariancetablica in, która po prostu zezwala na tablicę podtypów, może być przypisana do tablicy nadtypu, co zmusza do użycia rzutowania typu jawnego podczas próby odzyskania wartości powodując czas działania, ClassCastExceptionktóry jest jednym z głównych celów że generycy starają się wyeliminować: Silniejsze kontrole typów w czasie kompilacji .

Object[] stringArray = { "hi", "me" };
stringArray[1] = 1;
String aString = (String) stringArray[1]; // boom! the TypeCastException

Bardziej bezpośredni przykład można znaleźć w Effective Java: Pozycja 25 .


kowariancja : tablica typu S [] jest podtypem T [], jeśli S jest podtypem T

Hearen
źródło
0

Jeśli klasa używa jako sparametryzowanego typu, może zadeklarować tablicę typu T [], ale nie może bezpośrednio utworzyć takiej tablicy. Zamiast tego powszechnym podejściem jest tworzenie instancji tablicy typu Object [], a następnie wykonanie zwężenia rzutowania na typ T [], jak pokazano poniżej:

  public class Portfolio<T> {
  T[] data;
 public Portfolio(int capacity) {
   data = new T[capacity];                 // illegal; compiler error
   data = (T[]) new Object[capacity];      // legal, but compiler warning
 }
 public T get(int index) { return data[index]; }
 public void set(int index, T element) { data[index] = element; }
}
DeV
źródło
-2

Spróbuj tego:

List<?>[] arrayOfLists = new List<?>[4];
Jorge Washington
źródło