2012-05-02 14 views
15

Sto cercando di specificare che una classe generica deve essere una matrice, o meglio ancora una matrice primitiva. Finora questo è quello che ho ottenuto lavorando:Java generici e tipi di array, non è quello che stai pensando (ad esempio array di tipi generici)

interface Foo<T> { 
    void process(T data); 
} 

public class Moo implements Foo<int[]> { 
    void process(int[] data) { 
    // do stuff here 
    } 
} 

Questo è tutto il codice Java perfettamente valido e funziona perché gli array primitivi estendono oggetto. Si noti che questa domanda è completamente diversa da tutte le altre domande generiche sugli array Java che continuo a trovare. In quella situazione le persone vogliono creare un array con un tipo generico.

Il problema è che il tipo T può essere qualsiasi cosa che estende Object. Quello che voglio è quello di fare qualcosa di simile:

<T extends ONLY ARRAYS> 

o

<T extends ONLY PRIMITIVE ARRAYS>. 

è possibile?

MODIFICA: l'obiettivo finale sarebbe quello di aggiungere un controllo del tempo di compilazione sul tipo di array passato. Al momento qualsiasi oggetto vecchio può essere passato e verrà compilato correttamente. L'errore verrà rilevato solo in fase di esecuzione quando viene lanciata un'eccezione di classe cast. In realtà questo è il punto di forza di Generics in Java, per aggiungere un controllo più accurato del tipo a tempo di compilazione.

+2

Perché mai hai bisogno di fare questo? – Jeffrey

+0

Sono d'accordo sul fatto che "dovrebbe" essere possibile. Il mio istinto mi dice che non esiste nessun altro supertipo delle classi di primitive di Object. (C'è un isArray() sulla Classe Class) – esej

+0

Una singola interfaccia per l'elaborazione di immagini con dati di tipi primitivi diversi. L'alternativa è avere un'interfaccia diversa per ogni tipo, che è un peggior kludge. Esempio, Foo_S32, Foo_U8, Foo_F32, ... ecc. –

risposta

10

Non è possibile farlo. Nessuna classe può estendere un array, quindi non ci sarà mai un tipo che soddisfi il parametro generico T extends Object[]. (. Altro da sé Object[], ma allora non sarebbe utilizzando i generici)

cosa si potrebbe fare è qualcosa di simile:

public interface Foo<T extends Number> { 
    public void process(T[] data); 
} 

Ma allora si potrebbe incorrere in problemi di prestazioni con la boxe.

+4

Non proprio, gli array di un tipo specifico come String [] sono assegnabili a Object []. Ma questo non aiuta con gli array primitivi. –

+1

@StevenSchlansker L'esempio che ho dato è stato moot comunque, gli array non possono essere utilizzati come un parametro di tipo bound. – Jeffrey

+0

Hai ragione che l'approccio alternativo ucciderebbe le prestazioni. Dopo aver fatto un po 'più di ricerca, non riesco a trovare alcun tipo di eccezione di linguaggio speciale che consenta il tipo di vincolo che voglio. Selezionandola come la soluzione dato che sei stato il primo a dare la risposta corretta. –

1

No può fare. Nessuna interfaccia "interessante" né supertipo ma oggetto esiste per array di primitive:

public class SOTEST {  
    public static void main(String[] args) throws Exception { 
     int[] arr = new int[] {}; 
     Class c = arr.getClass(); 
     for(Class clazz:c.getInterfaces()) { 
      System.out.println(clazz.getName()); 
     } 

     System.out.println(c.getSuperclass().toString()); 
    } 
} 
8

In Java, i parametri di tipo possono only essere vincolati dalla relazione sottotipo, e gli unici supertipi comuni di tutte le matrici areObject, Clonable e Serializable. Il più vicino si può ottenere è per vincolare a Object[], che è il supertipo di tutti gli array con tipi di componenti non primitive, o forse a Number[], che è il supertipo di Integer[], Long[], ...

Anche se Java ha fatto supportare un tale vincolo, come faresti qualcosa di utile con quell'array? Non è possibile leggere singoli elementi poiché non è possibile dichiarare una variabile per contenere i risultati, né scrivere elementi individuali poiché non è possibile scrivere un'espressione assegnabile a un elemento dell'array.

Detto questo, mi piacerebbe impegnare la variabile di tipo per il tipo di componente, non il tipo array:

interface Foo<T extends Whatever> { 
    void process(T[] data); 
} 

perché si può fare riferimento a T[] conoscere T, ma conoscendo un T extends Object[] non ti permettere direttamente a fare riferimento al tipo di componente.

Edit: Jeffrey fa giustamente notare che tipo di matrice non può essere utilizzato nel tipo limiti, vale a dire <T extends Whatever[]> non si compila, in modo da avere sicuramente seguire il mio consiglio di dichiarare <T extends Whatever> e utilizzare T[] per fare riferimento al tipo di matrice.

+0

Il punto di voler aggiungere il vincolo non è che una classe generalizzata possa essere scritta manipolando l'array, ma per rendere l'API più semplice da utilizzare. Al momento non esiste un controllo del tempo di compilazione e gli errori verranno rilevati solo in fase di esecuzione. –

-2

Le matrici di X non hanno una gerarchia di tipi. Integer [] non è una sottoclasse di Number [].

per ottenere ciò che si desidera, utilizzare il tipo di componente di matrice come tipo di parametro T, e dichiarare i parametri e tipi restituiti come array di T.

+0

-1 per informazioni errate: 'Intero []' * è * un sottotipo di 'Numero []'. Per esempio, 'Number [] numbers = new Integer [] {1, 2, 3};' compila bene ... – meriton

+0

Bene: quello è pazzo, ed è un buco nel sistema di tipo java. Questo \t Integer [] ints = new Integer [] {1, 2, 3}; \t Numero [] numeri = pollici; \t numeri [1] = nuovo Float (4); \t per (int x: ints) { \t \t System.out.println (x); \t} compila e genera un'eccezione ArrayStore in fase di esecuzione. – PaulMurrayCbr

+1

Sì, la covarianza degli array viola la sostituibilità di liskov (a meno che non trattiamo ArrayStoreExceptions come parte del contratto di array, come fa il JLS). Tuttavia, il controllo di runtime assicura che Java rimanga sicuro durante il runtime. Per quanto sia "pazzo", hai certamente diritto alla tua opinione, ma penso che concentrarsi solo sulla scappatoia nel sistema dei tipi è una visione piuttosto ristretta. Gli array sono covarianti e vengono controllati in fase di runtime. Generics deve essere reso covariante in ogni sito di utilizzo e non viene controllato in fase di runtime (in realtà abilitando il danneggiamento dell'heap). Qual è il design migliore? – meriton