Wie erstelle ich ein generisches Array? [duplicate]

Lesezeit: 8 Minuten

Wie erstelle ich ein generisches Array duplicate
Benutzer2693979

Ich verstehe die Verbindung zwischen Generika und Arrays nicht.

Ich kann eine Array-Referenz mit generischem Typ erstellen:

private E[] elements; //GOOD

Kann aber kein Array-Objekt mit generischem Typ erstellen:

elements = new E[10]; //ERROR

Aber es funktioniert:

elements = (E[]) new Object[10]; //GOOD

  • Benutz einfach new ArrayList<E>()?

    – micha

    2. September 2013 um 21:34 Uhr

Wie erstelle ich ein generisches Array duplicate
Rohit Jain

Sie sollten Arrays und Generika nicht verwechseln. Sie passen nicht gut zusammen. Es gibt Unterschiede darin, wie Arrays und generische Typen die Typprüfung erzwingen. Wir sagen, dass Arrays verdinglicht sind, Generika jedoch nicht. Infolgedessen sehen Sie diese Unterschiede bei der Arbeit mit Arrays und Generika.

Arrays sind kovariant, Generics nicht:

Was das bedeutet? Sie müssen inzwischen wissen, dass die folgende Zuordnung gültig ist:

Object[] arr = new String[10];

Im Grunde ein Object[] ist ein Supertyp String[]da Object ist ein Supertyp String. Bei Generika ist dies nicht der Fall. Die folgende Deklaration ist also nicht gültig und wird nicht kompiliert:

List<Object> list = new ArrayList<String>(); // Will not compile.

Der Grund dafür ist, dass Generika unveränderlich sind.

Typenprüfung erzwingen:

Generics wurden in Java eingeführt, um eine stärkere Typprüfung zur Kompilierzeit zu erzwingen. Daher haben generische Typen zur Laufzeit aufgrund von keine Typinformationen Typ löschen. Also, ein List<String> hat einen statischen Typ von List<String> aber eine dynamische Art von List.

Arrays tragen jedoch die Laufzeittypinformationen des Komponententyps mit sich. Zur Laufzeit verwenden Arrays die Array Store-Prüfung, um zu überprüfen, ob Sie Elemente einfügen, die mit dem tatsächlichen Array-Typ kompatibel sind. Also folgender Code:

Object[] arr = new String[10];
arr[0] = new Integer(10);

wird gut kompiliert, schlägt aber zur Laufzeit aufgrund von ArrayStoreCheck fehl. Bei Generika ist dies nicht möglich, da der Compiler versucht, die Laufzeitausnahme zu verhindern, indem er eine Überprüfung der Kompilierzeit bereitstellt, indem er die Erstellung einer Referenz wie dieser vermeidet, wie oben gezeigt.

Also, was ist das Problem mit Generic Array Creation?

Erstellung eines Arrays, dessen Komponententyp entweder a Typparameterein konkreten parametrisierten Typ oder ein begrenzter parametrisierter Platzhaltertypist typunsicher.

Betrachten Sie den Code wie folgt:

public <T> T[] getArray(int size) {
    T[] arr = new T[size];  // Suppose this was allowed for the time being.
    return arr;
}

Da die Art von T zur Laufzeit nicht bekannt ist, ist das erstellte Array tatsächlich ein Object[]. Die obige Methode sieht also zur Laufzeit so aus:

public Object[] getArray(int size) {
    Object[] arr = new Object[size];
    return arr;
}

Angenommen, Sie rufen diese Methode folgendermaßen auf:

Integer[] arr = getArray(10);

Hier ist das Problem. Sie haben gerade eine zugewiesen Object[] zu einer Referenz von Integer[]. Der obige Code lässt sich problemlos kompilieren, schlägt jedoch zur Laufzeit fehl.

Aus diesem Grund ist die Erstellung generischer Arrays verboten.

Warum typisieren new Object[10] zu E[] funktioniert?

Jetzt Ihr letzter Zweifel, warum der folgende Code funktioniert:

E[] elements = (E[]) new Object[10];

Der obige Code hat die gleichen Auswirkungen wie oben erläutert. Wenn Sie es bemerken, gibt Ihnen der Compiler eine Ungeprüfte Cast-Warnung dort, da Sie in ein Array mit unbekanntem Komponententyp umwandeln. Das bedeutet, dass die Umwandlung zur Laufzeit fehlschlagen kann. Zum Beispiel, wenn Sie diesen Code in der obigen Methode haben:

public <T> T[] getArray(int size) {
    T[] arr = (T[])new Object[size];        
    return arr;
}

und Sie rufen es wie folgt auf:

String[] arr = getArray(10);

dies schlägt zur Laufzeit mit einer ClassCastException fehl. Also, nein, dieser Weg wird nicht immer funktionieren.

Was ist mit dem Erstellen eines Arrays von Typ List<String>[]?

Das Problem ist das gleiche. Aufgrund von Typlöschung, a List<String>[] ist nichts als ein List[]. Wenn also die Erstellung solcher Arrays erlaubt wäre, sehen wir uns an, was passieren könnte:

List<String>[] strlistarr = new List<String>[10];  // Won't compile. but just consider it
Object[] objarr = strlistarr;    // this will be fine
objarr[0] = new ArrayList<Integer>(); // This should fail but succeeds.

Jetzt wird der ArrayStoreCheck im obigen Fall zur Laufzeit erfolgreich sein, obwohl dies eine ArrayStoreException hätte auslösen sollen. Das liegt daran, beides List<String>[] und List<Integer>[] dazu kompiliert werden List[] zur Laufzeit.

Können wir also ein Array von unbegrenzten parametrisierten Platzhaltertypen erstellen?

Jawohl. Der Grund dafür ist, a List<?> ist ein verifizierbarer Typ. Und das macht Sinn, da überhaupt kein Typ zugeordnet ist. Durch Typlöschung geht also nichts verloren. Es ist also absolut typsicher, ein Array dieses Typs zu erstellen.

List<?>[] listArr = new List<?>[10];
listArr[0] = new ArrayList<String>();  // Fine.
listArr[1] = new ArrayList<Integer>(); // Fine

Sowohl der obige Fall ist in Ordnung, weil List<?> ist der Supertyp aller Instanziierungen des generischen Typs List<E>. Daher wird zur Laufzeit keine ArrayStoreException ausgegeben. Der Fall ist der gleiche wie beim Raw Types Array. Da Rohtypen auch reifizierbare Typen sind, können Sie ein Array erstellen List[].

Es geht also so, dass Sie nur ein Array von verifizierbaren Typen erstellen können, aber keine nicht verifizierbaren Typen. Beachten Sie, dass in allen oben genannten Fällen die Deklaration des Arrays in Ordnung ist, es ist die Erstellung des Arrays mit new Betreiber, der Probleme gibt. Es macht jedoch keinen Sinn, ein Array dieser Referenztypen zu deklarieren, da sie auf nichts anderes zeigen können null (Ignorieren der unbegrenzten Typen).

Gibt es eine Problemumgehung für E[]?

Ja, Sie können das Array mit erstellen Array#newInstance() Methode:

public <E> E[] getArray(Class<E> clazz, int size) {
    @SuppressWarnings("unchecked")
    E[] arr = (E[]) Array.newInstance(clazz, size);

    return arr;
}

Typumwandlung ist erforderlich, da diese Methode eine zurückgibt Object. Aber Sie können sicher sein, dass es sich um einen sicheren Wurf handelt. Sie können also sogar @SuppressWarnings für diese Variable verwenden.

  • Nitpick: “Erstellung eines Arrays, dessen Komponententyp … ein parametrisierter Platzhaltertyp ist, ist typunsicher.” Eigentlich Instanziieren zB a new List<?>[] { } ist gültig – es ist nur so, dass der Platzhalter nicht begrenzt werden kann.

    – Paul Bellora

    2. September 2013 um 23:46 Uhr


  • Auch “dies wird zur Laufzeit mit einer ClassCastException fehlschlagen.” ist nicht ganz richtig. Wenn eine Besetzung deaktiviert ist, bedeutet dies, dass sie es ist Gewohnheit scheitern schnell. Stattdessen ClassCastExceptions dürfen an anderen Stellen geworfen werden, an denen der Compiler während des Löschvorgangs Umwandlungen eingefügt hat. Dieses Problem ist ein gutes Beispiel.

    – Paul Bellora

    3. September 2013 um 0:25 Uhr


  • @Paul Bellora. Eigentlich meinte ich begrenzt. Das Wort verpasst. Wird editiert danke 🙂

    – Rohit Jain

    3. September 2013 um 4:55 Uhr

  • @Paul Bellora. Was den Casting-Teil angeht, habe ich das für das Casting geschrieben String[], das wird sicherlich scheitern. Bearbeitete diesen Teil, um es klarer zu machen.

    – Rohit Jain

    3. September 2013 um 4:57 Uhr


  • Oh, Generika waren so schlecht in Java implementiert.

    – IS4

    23. Februar 2016 um 20:48 Uhr

Hier ist die Umsetzung von LinkedList<T>#toArray(T[]):

public <T> T[] toArray(T[] a) {
    if (a.length < size)
        a = (T[])java.lang.reflect.Array.newInstance(
                            a.getClass().getComponentType(), size);
    int i = 0;
    Object[] result = a;
    for (Node<E> x = first; x != null; x = x.next)
        result[i++] = x.item;

    if (a.length > size)
        a[size] = null;

    return a;
}

Kurz gesagt, Sie können nur generische Arrays erstellen Array.newInstance(Class, int) wo int ist die Größe des Arrays.

1646252829 145 Wie erstelle ich ein generisches Array duplicate
Pshemo

Problem ist, dass während der Laufzeit generischer Typ wird gelöscht damit new E[10] wäre gleichbedeutend mit new Object[10].

Dies wäre gefährlich, da es möglich wäre, andere Daten als of in das Array einzufügen E Art. Aus diesem Grund müssen Sie den gewünschten Typ auch explizit angeben

  • Aber (E[]) wird nicht in (O[]) durch die Typlöschung?

    – Benutzer2693979

    2. September 2013 um 21:53 Uhr

  • @ user2693979 Das wird es. Generics sind Compiler-Tools, keine Runtime. Ich verstehe nicht ganz, welches Problem du darstellen willst…

    – Pschemo

    2. September 2013 um 22:02 Uhr


  • @ user2693979 Sie sollten Rohits Antwort akzeptieren, wenn Sie sie für besser halten. Kein Druck, nur weil ich meine etwas früher gepostet habe.

    – Pschemo

    2. September 2013 um 22:17 Uhr


  • Aber wenn E[] wird Objekt sein[] und (E[]) wird (Objekt[]), warum ist dann der Unterschied zwischen (e = new E[10]) und (e = (E[]) Objekt[10])? Werden nicht beide e = neues Objekt sein[10]?

    – Benutzer2693979

    2. September 2013 um 22:20 Uhr


  • @ user2693979 Das vermute ich new E[size] darf nicht verhindern, dass wir denken, dass wir tatsächlich eine Reihe von erstellen E Typ und nicht Object Art. Wege, die ich in meiner Antwort erwähnt habe, zeigen deutlich, was währenddessen vor sich geht new E[size] kann falsch interpretiert werden. Aber nochmal, das ist nur meine Vermutung.

    – Pschemo

    2. September 2013 um 22:28 Uhr

geprüft:

public Constructor(Class<E> c, int length) {

    elements = (E[]) Array.newInstance(c, length);
}

oder ungeprüft:

public Constructor(int s) {
    elements = new Object[s];
}

915830cookie-checkWie erstelle ich ein generisches Array? [duplicate]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy