Was ist der Grund, warum ich in Java keine generischen Array-Typen erstellen kann?

273

Was ist der Grund, warum Java dies nicht zulässt?

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

Ich konnte verstehen, dass .NET uns dies nicht erlaubt hat, da Sie in .NET Werttypen haben, die zur Laufzeit unterschiedliche Größen haben können, aber in Java sind alle Arten von T Objektreferenzen und haben daher dieselbe Größe ( korrigiere mich, wenn ich falsch liege).

Was ist der Grund?

verschlungenes Elysium
quelle
29
Worüber redest du? Sie können dies absolut in .NET tun. - Ich versuche hier herauszufinden, warum ich das in Java nicht kann.
BrainSlugs83
@ BrainSlugs83 - Bitte fügen Sie einen Link zu einem Codebeispiel oder Tutorial hinzu, das dies zeigt.
MasterJoe2
1
@ MasterJoe2 Der obige Code in der Frage des OP ist das, worauf ich mich beziehe. Es funktioniert gut in C #, aber nicht in Java. - Die Frage besagt, dass es in keinem von beiden funktioniert, was falsch ist. - Ich bin mir nicht sicher, ob es sinnvoll ist, darüber weiter zu diskutieren.
BrainSlugs83

Antworten:

204

Dies liegt daran, dass Javas Arrays (im Gegensatz zu Generika) zur Laufzeit Informationen über den Komponententyp enthalten. Sie müssen also den Komponententyp kennen, wenn Sie das Array erstellen. Da Sie nicht wissen, was Tzur Laufzeit läuft, können Sie das Array nicht erstellen.

newacct
quelle
29
Aber was ist mit Löschen? Warum trifft das nicht zu?
Qix - MONICA wurde am
14
Wie geht ArrayList <SomeType>das dann?
Thumbz
10
@ Thumbz: Du meinst new ArrayList<SomeType>()? Generische Typen enthalten zur Laufzeit nicht den Typparameter. Der Typparameter wird bei der Erstellung nicht verwendet. Es gibt keinen Unterschied im Code, der von new ArrayList<SomeType>()oder new ArrayList<String>()oder new ArrayList()überhaupt generiert wird .
Newacct
8
Ich habe mehr darüber gefragt, wie es ArrayList<T>funktioniert private T[] myArray. Irgendwo im Code muss es ein Array vom generischen Typ T haben. Wie also?
Thumbz
21
@ Thumbz: Es gibt kein Array vom Typ Laufzeit T[]. Es hat ein Array vom Typ Laufzeit Object[], und entweder 1) der Quellcode enthält eine Variable von Object[](so ist es in der neuesten Oracle Java-Quelle); oder 2) der Quellcode enthält eine Variable vom Typ T[], was eine Lüge ist, aber keine Probleme verursacht, da Tsie innerhalb des Bereichs der Klasse gelöscht wird.
Newacct
137

Zitat:

Arrays generischer Typen sind nicht zulässig, da sie nicht solide sind. Das Problem ist auf die Interaktion von Java-Arrays zurückzuführen, die nicht statisch einwandfrei sind, sondern dynamisch überprüft werden, mit Generika, die statisch einwandfrei und nicht dynamisch überprüft sind. So können Sie die Lücke ausnutzen:

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!
    }
}

Wir hatten vorgeschlagen, dieses Problem mit statisch sicheren Arrays (auch bekannt als Variance) zu lösen, die für Tiger abgelehnt wurden.

- Gafter

(Ich glaube, es ist Neal Gafter , bin mir aber nicht sicher)

Sehen Sie es hier im Kontext: http://forums.sun.com/thread.jspa?threadID=457033&forumID=316

Bart Kiers
quelle
3
Beachten Sie, dass ich es zu einem CW gemacht habe, da die Antwort nicht meine ist.
Bart Kiers
10
Dies erklärt, warum es möglicherweise nicht typsicher ist. Typensicherheitsprobleme können jedoch vom Compiler gewarnt werden. Tatsache ist, dass es nicht einmal möglich ist, es zu tun, aus fast dem gleichen Grund, warum Sie es nicht können new T(). Jedes Array in Java speichert standardmäßig den Komponententyp (dh T.class) darin. Daher benötigen Sie zur Laufzeit die Klasse T, um ein solches Array zu erstellen.
Newacct
2
Sie können immer noch verwenden new Box<?>[n], was manchmal ausreichend sein kann, obwohl es in Ihrem Beispiel nicht helfen würde.
Bartosz Klimek
1
@BartKiers Ich verstehe es nicht ... das wird immer noch nicht kompiliert (Java-8): Hat Box<String>[] bsa = new Box<String>[3];sich in Java-8 und höher etwas geändert, nehme ich an?
Eugene
1
@Eugene, Arrays bestimmter generischer Typen sind einfach nicht zulässig, da sie zu einem Verlust der Typensicherheit führen können, wie in der Stichprobe gezeigt. Es ist in keiner Java-Version zulässig. Die Antwort beginnt mit "Arrays von generischen Typen sind nicht erlaubt, weil sie nicht gesund sind."
Granat
47

Wenn Sie keine anständige Lösung anbieten, erhalten Sie meiner Meinung nach nur etwas Schlimmeres.

Die übliche Problemumgehung ist wie folgt.

T[] ts = new T[n];

wird ersetzt durch (vorausgesetzt, T erweitert Object und keine andere Klasse)

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

Ich bevorzuge das erste Beispiel, aber mehr akademische Typen scheinen das zweite zu bevorzugen oder ziehen es einfach vor, nicht darüber nachzudenken.

Die meisten Beispiele dafür, warum Sie ein Objekt [] nicht einfach verwenden können, gelten gleichermaßen für Liste oder Sammlung (die unterstützt werden), daher sehe ich sie als sehr schlechte Argumente an.

Hinweis: Dies ist einer der Gründe, warum die Sammlungsbibliothek selbst nicht ohne Warnungen kompiliert wird. Wenn Sie diesen Anwendungsfall nicht ohne Warnungen unterstützen können, ist mit dem Generika-Modell IMHO ein grundlegender Fehler aufgetreten.

Peter Lawrey
quelle
6
Mit dem zweiten muss man vorsichtig sein. Wenn Sie das so erstellte Array an jemanden zurückgeben, der beispielsweise a erwartet String[](oder wenn Sie es in einem öffentlich zugänglichen Feld speichern T[]und jemand es abruft), erhält er eine ClassCastException.
Newacct
4
Ich habe diese Antwort abgelehnt, weil Ihr bevorzugtes Beispiel in Java nicht zulässig ist und Ihr zweites Beispiel möglicherweise eine ClassCastException auslöst
José Roberto Araújo Júnior
5
@ JoséRobertoAraújoJúnior Es ist ziemlich klar, dass das erste Beispiel durch das zweite Beispiel ersetzt werden muss. Es wäre hilfreicher zu erklären, warum das zweite Beispiel eine ClassCastException auslösen kann, da dies nicht für alle offensichtlich ist.
Peter Lawrey
3
@ PeterLawrey Ich habe eine selbst beantwortete Frage erstellt, die zeigt, warum T[] ts = (T[]) new Object[n];eine schlechte Idee ist: stackoverflow.com/questions/21577493/…
José Roberto Araújo Júnior
1
@MarkoTopolnik Ich sollte eine Medaille für die Beantwortung all Ihrer Kommentare erhalten, um das zu erklären, was ich bereits gesagt habe. Das einzige, was sich von meinem ursprünglichen Grund geändert hat, ist, dass ich dachte, dass er T[] ts = new T[n];ein gültiges Beispiel war. Ich werde die Abstimmung behalten, weil seine Antwort anderen Entwicklern Probleme und Verwirrungen bereiten kann und auch nicht zum Thema gehört. Außerdem werde ich aufhören, dies zu kommentieren.
José Roberto Araújo Júnior
38

Arrays sind kovariant

Arrays werden als kovariant bezeichnet, was im Grunde bedeutet, dass ein Array vom Typ angesichts der Subtypisierungsregeln von Java T[]Elemente vom Typ Toder einen beliebigen Subtyp von enthalten kann T. Zum Beispiel

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

Aber nicht nur das, die Subtypisierungsregeln von Java besagen auch, dass ein Array S[]ein Subtyp des Arrays ist, T[]wenn Ses ein Subtyp von ist T, daher ist so etwas auch gültig:

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

Denn gemäß den Subtypisierungsregeln in Java ist ein Array Integer[]ein Subtyp eines ArraysNumber[] da Integer ein Subtyp von Number ist.

Diese Subtypisierungsregel kann jedoch zu einer interessanten Frage führen: Was würde passieren, wenn wir dies versuchen?

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

Diese letzte Zeile würde gut kompiliert werden, aber wenn wir diesen Code ausführen, würden wir eine bekommen ArrayStoreException weil wir versuchen, ein Double in ein Integer-Array zu setzen. Die Tatsache, dass wir über eine Zahlenreferenz auf das Array zugreifen, spielt hier keine Rolle. Entscheidend ist, dass das Array ein Array von Ganzzahlen ist.

Dies bedeutet, dass wir den Compiler täuschen können, aber wir können das Laufzeitsystem nicht täuschen. Und das ist so, weil Arrays ein reifizierbarer Typ sind. Dies bedeutet, dass Java zur Laufzeit weiß, dass dieses Array tatsächlich als Array von Ganzzahlen instanziiert wurde, auf die einfach über eine Referenz vom Typ zugegriffen wirdNumber[] .

Wie wir sehen können, ist eine Sache der tatsächliche Typ des Objekts, eine andere Sache ist die Art der Referenz, mit der wir darauf zugreifen, oder?

Das Problem mit Java Generics

Das Problem bei generischen Typen in Java besteht nun darin, dass die Typinformationen für Typparameter vom Compiler verworfen werden, nachdem die Codekompilierung abgeschlossen ist. Daher sind diese Typinformationen zur Laufzeit nicht verfügbar. Dieser Vorgang wird als Typlöschung bezeichnet . Es gibt gute Gründe, solche Generika in Java zu implementieren, aber das ist eine lange Geschichte und hat mit der Binärkompatibilität mit bereits vorhandenem Code zu tun.

Der wichtige Punkt hierbei ist, dass es zur Laufzeit keine Typinformationen gibt und es keine Möglichkeit gibt, sicherzustellen, dass wir keine Haufenverschmutzung begehen.

Betrachten wir nun den folgenden unsicheren Code:

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

Wenn der Java-Compiler uns nicht davon abhält, kann uns das Laufzeitsystem auch nicht aufhalten, da zur Laufzeit nicht festgestellt werden kann, dass diese Liste nur eine Liste von Ganzzahlen sein sollte. Die Java-Laufzeit würde es uns ermöglichen, alles, was wir wollen, in diese Liste aufzunehmen, wenn sie nur Ganzzahlen enthalten sollte, da sie bei ihrer Erstellung als Liste von Ganzzahlen deklariert wurde. Aus diesem Grund lehnt der Compiler Zeile 4 ab, da sie unsicher ist und, wenn dies zulässig ist, die Annahmen des Typsystems verletzen könnte.

Daher haben die Designer von Java dafür gesorgt, dass wir den Compiler nicht täuschen können. Wenn wir den Compiler nicht täuschen können (wie wir es mit Arrays tun können), können wir auch das Laufzeitsystem nicht täuschen.

Daher sagen wir, dass generische Typen nicht überprüfbar sind, da wir zur Laufzeit die wahre Natur des generischen Typs nicht bestimmen können.

Ich habe einige Teile dieser Antworten übersprungen. Den vollständigen Artikel finden Sie hier: https://dzone.com/articles/covariance-and-contravariance

Humoyun Ahmad
quelle
32

Der Grund dafür ist, dass Java seine Generics nur auf Compilerebene implementiert und für jede Klasse nur eine Klassendatei generiert wird. Dies wird als Typlöschung bezeichnet .

Zur Laufzeit muss die kompilierte Klasse alle ihre Verwendungen mit demselben Bytecode behandeln. Hätte new T[capacity]also absolut keine Ahnung, welcher Typ instanziiert werden muss.

Durandal
quelle
17

Die Antwort wurde bereits gegeben, aber wenn Sie bereits eine Instanz von T haben, können Sie dies tun:

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

Hoffe, ich könnte helfen, Ferdi265

Ferdi265
quelle
1
Das ist eine schöne Lösung. Dies führt jedoch zu ungeprüften Warnungen (von Objekt nach T [] umgewandelt). Eine andere "langsamere", aber "warnungsfreie" Lösung wäre : T[] ts = t.clone(); for (int i=0; i<ts.length; i++) ts[i] = null;.
Mitternacht
1
Wenn wir das behalten T[] twürden, wäre es das auch (T[]) Array.newInstance(t.getClass().getComponentType(), length);. Ich habe einige Zeit damit verbracht, es herauszufinden getComponentType(). Hoffe das hilft anderen.
Mitternacht
1
@midnite t.clone()wird nicht zurückkehren T[]. Weil tin dieser Antwort kein Array ist.
Xmen
6

Der Hauptgrund liegt in der Tatsache, dass Arrays in Java kovariant sind.

Es gibt einen guten Überblick hier .

GaryF
quelle
Ich sehe nicht ein, wie Sie "new T [5]" auch mit invarianten Arrays unterstützen könnten.
Dimitris Andreou
2
@ DimitrisAndreou Nun, das Ganze ist eher eine Komödie von Fehlern im Java-Design. Alles begann mit Array-Kovarianz. Dann, sobald Sie Matrixkovarianz haben, können Sie werfen String[]auf Objectund speichern Sie ein Integerin ihm. Dann mussten sie eine Laufzeit-Typprüfung für Array-Speicher ( ArrayStoreException) hinzufügen, da das Problem zur Kompilierungszeit nicht erkannt werden konnte. (Andernfalls Integerkönnte ein tatsächlich in einem stecken bleiben String[], und Sie würden eine Fehlermeldung erhalten, wenn Sie versuchen, es abzurufen, was schrecklich wäre.) ...
Radon Rosborough
2
@DimitrisAndreou… Sobald Sie eine Laufzeitprüfung anstelle einer weitaus solideren Prüfung zur Kompilierungszeit durchgeführt haben, stoßen Sie auf eine Typlöschung (ebenfalls ein unglücklicher Konstruktionsfehler - nur aus Gründen der Abwärtskompatibilität enthalten). Typ Löschung bedeutet , dass Sie nicht können Laufzeittypprüfungen für generische Typen tun. Um das Problem mit dem Array-Speichertyp zu vermeiden, können Sie einfach keine generischen Arrays verwenden. Wenn sie Arrays in erster Linie einfach invariant gemacht hätten, könnten wir nur Typprüfungen zur Kompilierungszeit durchführen, ohne die Löschung zu beeinträchtigen.
Radon Rosborough
… Ich habe gerade die fünfminütige Bearbeitungszeit für Kommentare entdeckt. Objecthätte Object[]in meinem ersten Kommentar sein sollen.
Radon Rosborough
3

Ich mag die indirekte Antwort von Gafter . Ich schlage jedoch vor, dass es falsch ist. Ich habe Gafters Code ein wenig geändert. Es kompiliert und läuft eine Weile, dann bombardiert es dort, wo Gafter es vorhergesagt hat

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();
        }
    }
}

Die Ausgabe ist

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)

Mir scheint also, dass Sie generische Array-Typen in Java erstellen können. Habe ich die Frage falsch verstanden?

Emory
quelle
Ihr Beispiel unterscheidet sich von dem, was ich gefragt habe. Was Sie beschrieben haben, sind die Gefahren der Array-Kovarianz. Probieren Sie es aus (für .NET: blogs.msdn.com/b/ericlippert/archive/2007/10/17/… )
verschlungenes Elysium
Hoffentlich erhalten Sie vom Compiler eine Typ-Sicherheitswarnung, ja?
Matt McHenry
1
Ja, ich erhalte eine Sicherheitswarnung. Ja, ich sehe, dass mein Beispiel nicht auf die Frage reagiert.
Emory
Tatsächlich erhalten Sie mehrere Warnungen aufgrund der schlampigen Initialisierung von a, b, c. Dies ist auch bekannt und wirkt sich auf die Kernbibliothek aus, z. B. <T> java.util.Arrays.asList (T ...). Wenn Sie einen nicht überprüfbaren Typ für T übergeben, erhalten Sie eine Warnung (da das erstellte Array einen weniger genauen Typ hat, als der Code vorgibt), und es ist super hässlich. Es ist besser, wenn der Autor dieser Methode die Warnung erhält, anstatt sie an der Verwendungsstelle auszugeben, da die Methode selbst sicher ist und das Array dem Benutzer nicht zur Verfügung stellt.
Dimitris Andreou
1
Sie haben hier kein generisches Array erstellt. Der Compiler hat ein (nicht generisches) Array für Sie erstellt.
Newacct
2

Ich weiß, dass ich etwas spät zur Party hier komme, aber ich dachte, ich könnte zukünftigen Googlern helfen, da keine dieser Antworten mein Problem behoben hat. Die Antwort von Ferdi265 hat jedoch immens geholfen.

Ich versuche, meine eigene verknüpfte Liste zu erstellen, daher hat der folgende Code für mich funktioniert:

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;
    }  

In der toArray () -Methode liegt die Möglichkeit, ein Array eines generischen Typs für mich zu erstellen:

TYPE[] result = (TYPE[])Array.newInstance(header.getObject().getClass(),size());    
Derek Ziemba
quelle
2

In meinem Fall wollte ich einfach eine Reihe von Stapeln, ungefähr so:

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

Da dies nicht möglich war, habe ich Folgendes als Problemumgehung verwendet:

  1. Erstellt eine nicht generische Wrapper-Klasse um Stack (z. B. MyStack).
  2. MyStack [] stacks = new MyStack [2] hat einwandfrei funktioniert

Hässlich, aber Java ist glücklich.

Hinweis: Wie von BrainSlugs83 im Kommentar zur Frage erwähnt, ist es durchaus möglich, Arrays von Generika in .NET zu haben

David Airapetyan
quelle
2

Aus dem Oracle-Tutorial :

Sie können keine Arrays parametrisierter Typen erstellen. Der folgende Code wird beispielsweise nicht kompiliert:

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

Der folgende Code zeigt, was passiert, wenn verschiedene Typen in ein Array eingefügt werden:

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

Wenn Sie dasselbe mit einer generischen Liste versuchen, liegt ein Problem vor:

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.

Wenn Arrays mit parametrisierten Listen zulässig wären, würde der vorherige Code die gewünschte ArrayStoreException nicht auslösen.

Für mich klingt es sehr schwach. Ich denke, dass jeder mit einem ausreichenden Verständnis von Generika vollkommen in Ordnung wäre und sogar erwarten würde, dass die ArrayStoredException in einem solchen Fall nicht ausgelöst wird.

Stick Hero
quelle
0

Es muss sicherlich einen guten Weg geben (vielleicht mit Reflexion), denn es scheint mir, dass genau das der ArrayList.toArray(T[] a)Fall ist. Ich zitiere:

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

Gibt ein Array zurück, das alle Elemente in dieser Liste in der richtigen Reihenfolge enthält. Der Laufzeittyp des zurückgegebenen Arrays ist der des angegebenen Arrays. Wenn die Liste in das angegebene Array passt, wird sie darin zurückgegeben. Andernfalls wird ein neues Array mit dem Laufzeittyp des angegebenen Arrays und der Größe dieser Liste zugewiesen.

Eine Möglichkeit, dies zu umgehen, besteht darin, diese Funktion zu verwenden, dh eines ArrayListder gewünschten Objekte im Array toArray(T[] a)zu erstellen und dann das eigentliche Array zu erstellen. Es wäre nicht schnell, aber Sie haben Ihre Anforderungen nicht erwähnt.

Weiß also jemand, wie toArray(T[] a)es umgesetzt wird?

Adam
quelle
3
List.toArray (T []) funktioniert, weil Sie ihm zur Laufzeit im Wesentlichen den Komponententyp T geben (Sie geben ihm eine Instanz des gewünschten Array-Typs, von der es die Array-Klasse und dann die Komponentenklasse T abrufen kann ). Mit dem tatsächlichen Komponententyp zur Laufzeit können Sie jederzeit ein Array dieses Laufzeittyps mit erstellen Array.newInstance(). Sie werden feststellen, dass dies in vielen Fragen erwähnt wird, in denen gefragt wird, wie ein Array mit einem zur Kompilierungszeit unbekannten Typ erstellt werden soll. Aber das OP fragte speziell, warum Sie die new T[]Syntax nicht verwenden können , was eine andere Frage ist
newacct
0

Dies liegt daran, dass Generika zu Java hinzugefügt wurden, nachdem sie erstellt wurden. Daher ist es etwas umständlich, da die ursprünglichen Hersteller von Java dachten, dass beim Erstellen eines Arrays der Typ bei der Erstellung angegeben werden würde. Das funktioniert also nicht mit Generika, also müssen Sie E [] array = (E []) new Object [15] ausführen; Dies wird kompiliert, gibt aber eine Warnung aus.

Alvin
quelle
0

Wenn wir keine generischen Arrays instanziieren können, warum hat die Sprache generische Array-Typen? Was bringt es, einen Typ ohne Objekte zu haben?

Der einzige Grund, an den ich denken kann, ist varargs - foo(T...). Andernfalls hätten sie generische Array-Typen vollständig bereinigen können. (Nun, sie mussten kein Array für Varargs verwenden, da Varargs vor 1.5 nicht existierten . Das ist wahrscheinlich ein weiterer Fehler.)

Es ist also eine Lüge, Sie können generische Arrays durch Varargs instanziieren!

Natürlich sind die Probleme mit generischen Arrays immer noch real, z

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!
}

Wir können dieses Beispiel verwenden, um die Gefahr eines generischen Arrays tatsächlich zu demonstrieren .

Andererseits verwenden wir seit einem Jahrzehnt generische Varargs, und der Himmel fällt noch nicht. Wir können also argumentieren, dass die Probleme übertrieben sind; es ist keine große Sache. Wenn die explizite generische Array-Erstellung zulässig ist, treten hier und da Fehler auf. Aber wir sind an die Probleme der Löschung gewöhnt und können damit leben.

Und wir können darauf hinweisen, foo2die Behauptung zu widerlegen, dass die Spezifikation uns von den Problemen abhält, von denen sie behaupten, uns abzuhalten. Wenn Sun mehr Zeit und Ressourcen für 1,5 hätte , hätten sie meiner Meinung nach eine zufriedenstellendere Lösung finden können.

ZhongYu
quelle
0

Wie bereits erwähnt, können Sie natürlich über einige Tricks erstellen .

Aber es wird nicht empfohlen.

Weil das Löschen des Typs und vor allem das covarianceIn-Array, das nur ein Subtyp-Array zulässt, einem Supertyp-Array zugewiesen werden kann, wodurch Sie gezwungen sind, explizite Typumwandlungen zu verwenden, wenn Sie versuchen, den Wert zurückzugewinnen, was zur Laufzeit führt, ClassCastExceptionwas eines der Hauptziele ist Diese Generika versuchen zu beseitigen: Stärkere Typprüfungen zur Kompilierungszeit .

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

Ein direkteres Beispiel finden Sie in Effective Java: Punkt 25 .


Kovarianz : Ein Array vom Typ S [] ist ein Subtyp von T [], wenn S ein Subtyp von T ist

Gehört
quelle
0

Wenn die Klasse einen parametrisierten Typ verwendet, kann sie ein Array vom Typ T [] deklarieren, ein solches Array jedoch nicht direkt instanziieren. Stattdessen besteht ein gängiger Ansatz darin, ein Array vom Typ Object [] zu instanziieren und dann eine Verengung in Typ T [] vorzunehmen, wie im Folgenden gezeigt:

  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
quelle
-2

Versuche dies:

List<?>[] arrayOfLists = new List<?>[4];
Jorge Washington
quelle