Domanda Array generici in Java


OK, sto navigando su google e non riesco a trovare alcuna soluzione al mio problema. Ho trovato molte soluzioni, ma non tutte quelle che si adattano.

Devo creare una serie di farmaci generici. Ma il tipo generico stesso estende Paragonabile. Quando provo il seguente:

public class Hash<T extends Comparable<String>> {
    private T[] hashTable;
    private int tableSize;

    Hash(int records, double load) {
        tableSize = (int)(records / loadFactor);
        tableSize = findNextPrime(tableSize);
        hashTable = (T[])(new Object[tableSize]);  //Error: Ljava.lang.Object; cannot be cast to [Ljava.lang.Comparable;
    }
}

Il problema è che l'oggetto non può essere lanciato come un generico che estende Paragonabile. C'è un modo per aggirare questo?


44
2017-11-30 01:55


origine


risposte:


Generici e matrici non si mescolano, fondamentalmente. La risposta breve è che puoi risolvere questo problema. La risposta più lunga è che probabilmente non dovresti e spiegherò perché.

Potresti usare Array.newInstance() come questo:

private Comparable[] hashtable;

...

hashtable = (Comparable[])Array.newInstance(Comparable.class, tableSize);

ma tu non si può crea una matrice del tuo tipo parametrico.

Gli array sono covariante. Ciò significa che mantengono il tipo dei loro elementi in fase di runtime. I generici di Java non lo sono. Usano tipo cancellato per mascherare fondamentalmente il casting implicito che sta succedendo. È importante capirlo.

Quindi, quando crei una matrice di oggetti non puoi lanciarla su, diciamo, una matrice comparabile (o qualsiasi altro tipo) perché non è corretta.

Per darti un esempio. Con i generici questo è perfettamente legale:

List<String> list = new ArrayList<String>();
List<Integer> list2 = (List<Integer>)list;
list.add(3);

È anche per questo che non puoi farlo:

public <T> T newInstance(T t) {
  return new T(); // error!
}

cioè a runtime non c'è conoscenza della classe di T. Questo è il motivo per cui il codice sopra riportato è più spesso scritto come:

public <T> T newInstance(T t, Class<T> clazz) {
  return clazz.newInstance();
}

perché non è un tipo runtime per l'argomento generico. Ma con gli array:

String arr[] = new String[10];
Integer arr2[] = (Integer[])arr; // error!

Quello che dovresti fare in questo caso (imho) non è usare matrici ma usando un ArrayList. In tutta onestà, ci sono ben poche ragioni per usare gli array su un ArrayList e i generici sono solo un esempio di ciò.

Per una spiegazione migliore e più completa vedi la (eccellente) Domande frequenti su Java Generics:

Posso creare un array il cui tipo di componente è un tipo parametrico concreto?

No, perché non è sicuro dal tipo.

Gli array sono covarianti, il che significa che   una serie di riferimenti supertipi è a   supertipo di una matrice di sottotipo   Riferimenti. Questo è, Object[] è un   supertipo di String[] e una stringa   l'array è accessibile tramite a   variabile di riferimento di tipo Object[].

...


76
2017-11-30 02:05



Le altre risposte qui generalmente propongono un approccio migliore per questo (in particolare la raccomandazione di utilizzare un ArrayList), ma una risposta semplice in questo caso specifico potrebbe essere quella di fare:

hashTable = (T[])(new Comparable[tableSize]);

(ad esempio, crea una matrice di tipo Comparable raw anziché Object)

Se incapsuli correttamente tutti gli accessi a questo array all'interno dell'oggetto Hash, questo dovrebbe funzionare, ma (come spiegano le altre risposte) potresti lasciarti vulnerabile.


16
2017-11-30 02:13



Il cast che stai tentando

(T[])(new Object[tableSize]);

fallisce, perché gli elementi nell'array sono istanze di Object. L'oggetto non si estende Comparable<String>, quindi il cast (T []) fallisce perché T è definito come:

T extends Comparable<String>

Per risolvere questo problema:

  • Crea un'istanza della matrice in modo che gli elementi siano istanze di alcune classi estese Comparable<String>
  • Modificare hashTable da una matrice (che non è un tipo generico), ad un tipo di raccolta generico, ad es. List<T> hashTable = new ArrayList<T>(tableSize>)

4
2017-11-30 02:12



Spesso si verificano dei problemi quando è necessario creare un'istanza di un tipo generico. Il modo più semplice per aggirare questo è quello di passare la classe di effettivamente verrà memorizzata nel costruttore. In questo modo puoi costruire dal tipo reale. Prova qualcosa del genere:

public class Hash<T extends Comparable<String>>
{
  Hash(int records, double load, Class<T> class)
  {
    tableSize = (int)(records / loadFactor);
    tableSize = findNextPrime(tableSize);

    hashTable = java.lang.reflect.Array.newInstance(class, tableSize);
  }

private T[] hashTable;
private int tableSize;

}

1
2017-11-30 02:10



Il cast forzato suggerito da altre persone non ha funzionato per me, lanciando un'eccezione di casting illegale.

Tuttavia, questo cast implicito ha funzionato bene:

Item<K>[] array = new Item[SIZE];

dove Item è una classe che ho definito contenente il membro:

private K value;

In questo modo si ottiene una matrice di tipo K (se l'elemento ha solo il valore) o qualsiasi tipo generico che si desidera definire nell'elemento di classe.


0
2017-09-14 21:30