Wat is de reden dat ik geen generieke arraytypes kan maken in Java?

Wat is de reden waarom Java ons dit niet toestaat

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

Ik kon begrijpen dat .NET ons niet toestond om dat te doen, omdat je in .NET waardetypes hebt die tijdens runtime verschillende groottes kunnen hebben, maar in Java zullen alle soorten T objectreferenties zijn, dus met de dezelfde maat (corrigeer me als ik het mis heb).

Wat is de reden?


Antwoord 1, autoriteit 100%

Dat komt omdat Java’s arrays (in tegenstelling tot generieke) tijdens runtime informatie bevatten over het type component. U moet dus het componenttype weten wanneer u de array maakt. Aangezien u niet weet wat Tis tijdens runtime, kunt u de array niet maken.


Antwoord 2, autoriteit 64%

Citaat:

Arrays van generieke typen zijn dat niet
toegestaan omdat ze niet gezond zijn. De
probleem is te wijten aan de interactie van
Java-arrays, die niet statisch zijn
geluid, maar worden dynamisch gecontroleerd,
met generieke geneesmiddelen, die statisch zijn
geluid en niet dynamisch gecontroleerd.
Hier leest u hoe u de
maas in de wet:

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

We hadden voorgesteld dit op te lossen
probleem met statisch veilige arrays
(ook bekend als Variance) maar die werd afgewezen
voor Tiger.

gafter

(Volgens mij is het Neal Gafter, maar weet het niet zeker)

Bekijk het hier in context: http://forums.sun. com/thread.jspa?threadID=457033&forumID=316


Antwoord 3, autoriteit 24%

Door geen fatsoenlijke oplossing te bieden, eindig je met iets ergers IMHO.

De gebruikelijke oplossing is als volgt.

T[] ts = new T[n];

wordt vervangen door (ervan uitgaande dat T Object uitbreidt en niet een andere klasse)

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

Ik geef de voorkeur aan het eerste voorbeeld, hoewel meer academische types de voorkeur geven aan het tweede, of er gewoon liever niet aan denken.

De meeste voorbeelden van waarom je niet gewoon een object kunt gebruiken[] zijn evenzeer van toepassing op lijst of verzameling (die worden ondersteund), dus ik zie ze als zeer slechte argumenten.

Opmerking: dit is een van de redenen waarom de collecties-bibliotheek zelf niet zonder waarschuwingen compileert. Als je deze use-case niet zonder waarschuwingen kunt ondersteunen, is er iets fundamenteel gebroken met het generieke model IMHO.


Antwoord 4, autoriteit 16%

De reden dat dit onmogelijk is, is dat Java zijn Generics puur op compilerniveau implementeert en er voor elke klasse slechts één klassebestand wordt gegenereerd.
Dit wordt Type wissengenoemd.

Bij runtime heeft de gecompileerde klasse nodig om al zijn toepassingen met dezelfde bytecode aan te pakken. Dus, new T[capacity]zou absoluut geen idee hebben welk type moet worden geïnstantieerd.


Antwoord 5, Autoriteit 9%

Het antwoord is al gegeven, maar als u al een exemplaar van t hebt, dan kunt u dit doen:

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

Hoop, ik zou kunnen helpen,
FERDI265


Antwoord 6, Autoriteit 3%

De belangrijkste reden is te wijten aan het feit dat arrays in Java Covariant zijn.

Er is een goed overzicht hier .


Antwoord 7

Ik hou van het antwoord indirect gegeven
door gafer . Ik stel echter voor dat het verkeerd is. Ik heb de code van Gafer een beetje veranderd. Het compileert en het loopt een tijdje en bombardeert het waar GAFTER voorspelde dat het zou doen

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

De uitvoer is

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)

Het lijkt me dus dat je generieke arraytypes in Java kunt maken. Heb ik de vraag verkeerd begrepen?


Antwoord 8

Ik weet dat ik een beetje laat op het feest ben, maar ik dacht dat ik toekomstige googlers misschien zou kunnen helpen, aangezien geen van deze antwoorden mijn probleem heeft opgelost. Het antwoord van Ferdi265 hielp echter enorm.

Ik probeer mijn eigen gelinkte lijst te maken, dus de volgende code werkte voor mij:

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 de methode toArray() ligt de manier om voor mij een array van een generiek type te maken:

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

Antwoord 9

In mijn geval wilde ik gewoon een array van stapels, zoiets als dit:

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

Omdat dit niet mogelijk was, gebruikte ik het volgende als tijdelijke oplossing:

  1. Een niet-generieke wrapper-klasse gemaakt rond Stack (zeg MyStack)
  2. MyStack[]-stacks = nieuwe MyStack[2] werkte perfect

Lelijk, maar Java is blij.

Opmerking: zoals vermeld door BrainSlugs83 in de opmerking bij de vraag, is het heel goed mogelijk om arrays van generieke geneesmiddelen in .NET te hebben


Antwoord 10

Van Oracle-zelfstudie:

U kunt geen arrays van geparametriseerde typen maken. De volgende code compileert bijvoorbeeld niet:

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

De volgende code illustreert wat er gebeurt als verschillende typen in een array worden ingevoegd:

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

Als je hetzelfde probeert met een generieke lijst, zou er een probleem zijn:

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.

Als de reeksen van geparametreerde lijsten waren toegestaan, zou de vorige code niet de gewenste arraystoreException gooien.

Naar mij klinkt het erg zwak. Ik denk dat iemand met een voldoende begrip van generiek perfect goed zou zijn, en zelfs verwacht, dat de arraystoredexception niet in een dergelijk geval wordt gegooid.


Antwoord 11

Het is omdat Generics aan Java werden toegevoegd nadat ze het hebben gemaakt, dus het is een beetje onhandig omdat de originele makers van Java dachten dat bij het maken van een array het type zou worden gespecificeerd in het maken ervan. Dus dat werkt niet met Generics, dus je moet doen
E [] array = (e []) Nieuw object [15];
Dit compileert, maar het geeft een waarschuwing.


Antwoord 12

Er moet zeker een goede manier omheen zijn (misschien met behulp van reflectie), omdat het mij lijkt dat dat precies is wat ArrayList.toArray(T[] a)doet. Ik citeer:

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

Retourneert een array die alle van de
elementen in deze lijst in de juiste volgorde; het runtime-type van de
Geretourneerde array is dat van de opgegeven array. Als de lijst past in de
gespecificeerde array, het wordt daarin geretourneerd. Anders is een nieuwe array
toegewezen met het runtime-type van de opgegeven array en de grootte van
deze lijst.

Een manier om dit te omzeilen zou zijn om deze functie te gebruiken, dwz een ArrayListmaken van de objecten die u in de array wilt hebben, en vervolgens toArray(T[] a)gebruiken om de eigenlijke array te maken. Het zou niet snel zijn, maar je hebt je vereisten niet vermeld.

Dus weet iemand hoe toArray(T[] a)wordt geïmplementeerd?


Antwoord 13

Als we geen generieke arrays kunnen instantiëren, waarom heeft de taal dan generieke arraytypes? Wat heeft het voor zin om een type zonder objecten te hebben?

De enige reden die ik kan bedenken, is varargs – foo(T...). Anders hadden ze generieke arraytypen volledig kunnen wissen. (Nou, ze hoefden array niet echt te gebruiken voor varargs, aangezien varargs niet bestonden vóór 1.5. Dat is waarschijnlijk een andere fout.)

Het is dus een leugen dat je kuntgenerieke arrays instantiëren, via varargs!

Natuurlijk zijn de problemen met generieke arrays nog steeds reëel, bijvoorbeeld

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

We kunnen dit voorbeeld gebruiken om het gevaar van een generiekearray daadwerkelijk aan te tonen.

Aan de andere kant gebruiken we al tien jaar generieke varargs en de lucht valt nog niet. We kunnen dus stellen dat de problemen worden overdreven; het is geen probleem. Als expliciete generieke array-creatie is toegestaan, hebben we hier en daar bugs; maar we zijn gewend aan de problemen van wissen, en we kunnen ermee leven.

En we kunnen verwijzen naar foo2om de bewering te weerleggen dat de specificatie ons weerhoudt van de problemen waarvan ze beweren dat ze ons ervan weerhouden. Als Sun meer tijd en middelen had gehad voor 1.5, denk ik dat ze tot een bevredigender oplossing hadden kunnen komen.


Antwoord 14

Zoals anderen al zeiden, kun je natuurlijk creëren via enkele trucjes.

Maar het wordt niet aanbevolen.

Omdat de verwijdering typten nog belangrijker de covariancein array die alleen een subtype-array toestaat, kan worden toegewezen aan een supertype-array, wat je dwingt om expliciete typecast te gebruiken wanneer je probeert de waarde terug te krijgen, waardoor runtime ClassCastExceptionwat een van de belangrijkste doelstellingen is die generieke geneesmiddelen proberen te elimineren: Strongere typecontroles tijdens het compileren.

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

Een directer voorbeeld is te vinden in Effectieve Java: item 25.


covariantie: een array van het type S[] is een subtype van T[] als S een subtype van T is


Antwoord 15

Als de klasse gebruikt als een geparametreerd type, kan deze een reeks type t [] declareren, maar het kan niet direct een dergelijke array instantiëren. In plaats daarvan is een gemeenschappelijke aanpak om een ​​reeks typeobject [] te instantiëren en vervolgens een versmalling te maken om TYPE T [] te typen, zoals weergegeven in het volgende:

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

Antwoord 16

t Vals []; // ok

Maar je kunt geen reeks t
// Vals = NIEUW T [10]; // kan geen reeks t

maken

De reden waarom u geen reeks t kunt maken, is dat er geen manier is voor de
Compiler om te weten welk type array daadwerkelijk is gemaakt.


Antwoord 17

Probeer dit:

List<?>[] arrayOfLists = new List<?>[4];

Other episodes