2009-04-25 12 views
6

Sto giocando con alcuni code kata e cercando di ottenere una migliore comprensione dei java generici allo stesso tempo. Ho questo piccolo metodo che stampa array come mi piace vederli e ho un paio di metodi helper che accettano una serie di "cose" e un indice e restituisce l'array delle "cose" sopra o sotto l'indice (è un algoritmo di ricerca binaria).Puoi passare un array int a un metodo generico in java?

Due domande,

# 1 Posso evitare il cast a T in splitBottom e splitTop? Non mi sembra giusto, o sto andando in questo modo nel modo sbagliato (non dirmi di usare python o qualcosa del genere ..;))

# 2 Devo scrivere metodi separati per trattare i primitivi matrici o c'è una soluzione migliore?

public class Util { 

    public static <T> void print(T[] array) { 
     System.out.print("{"); 
     for (int i = 0; i < array.length; i++) { 
      System.out.print(array[i]); 
      if (i < array.length - 1) { 
       System.out.print(", "); 
      } 
     } 
     System.out.println("}"); 
    } 

    public static <T> T[] splitTop(T[] array, int index) { 
     Object[] result = new Object[array.length - index - 1]; 
     System.arraycopy(array, index + 1, result, 0, result.length); 
     return (T[]) result; 
    } 

    public static <T> T[] splitBottom(T[] array, int index) { 
     Object[] result = new Object[index]; 
     System.arraycopy(array, 0, result, 0, index); 
     return (T[]) result; 
    } 

    public static void main(String[] args) { 

     Integer[] integerArray = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 
     print(integerArray); 
     print(splitBottom(integerArray, 3)); 
     print(splitTop(integerArray, 3)); 

     String[] stringArray = {"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"}; 
     print(stringArray); 
     print(splitBottom(stringArray, 3)); 
     print(splitTop(stringArray, 3)); 

     int[] intArray = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 
     // ??? 
    } 
} 

risposta

9

I farmaci generici non trattano i primitivi in ​​modo coerente. Questo perché i generici non sono come i modelli in C++, è solo un'aggiunta di tempo di compilazione a una singola classe.

Quando il generico viene compilato, si finisce con Oggetto [] nell'esempio precedente come tipo di implementazione. Come int [] e byte [] etc, non estendere Object [] non puoi usarli in modo intercambiabile anche se il codice in questione sarebbe identico (di nuovo i generici non sono template)

L'unica classe int [] e Object [] share is Object. È possibile scrivere i metodi sopra Oggetto come tipo (consultare System.arraycopy, Array.getLength, Array.get, Array.set)

1

Domanda 1: Gli array di casting non funzionano come previsto. Una stringa è un oggetto, ma una matrice di stringhe non è una matrice di oggetti.

Provare a usare qualcosa come:

public static <T> T[] splitTop(T[] array, int index) { 
    T[] result = Arrays.copyOfRange(array, index + 1, array.length); 
    return result; 
} 

Domanda 2: Per gli array di primitive mia funzione, ovviamente, non funziona neanche. Non c'è una soluzione elegante ad esso - guarda ad esempio la libreria di Array che ha diverse copie essenzialmente dello stesso metodo per ogni tipo di matrice primitiva.

+0

Non è possibile farlo con i generici, che si traduce in un errore di tipo incompatibile. – hbw

+0

@htw puoi spiegare di più? – blank

3

1 Posso evitare il cast a T in splitBottom e splitTop? Non sente bene, o io vado su questo nel modo sbagliato (non ditemi di usare pitone o qualcosa del genere ..;))

Non solo si può non evitarlo, ma non dovresti farlo In Java, diversi tipi di array sono in realtà diversi tipi di runtime. Un array creato come Object[] non può essere assegnato a una variabile di AnythingElse []. Il cast non fallirà immediatamente, perché nei generici il tipo T viene cancellato, ma in seguito genererà una classe ClassCastException quando il codice lo prova ad usarlo come qualcosa [] come promesso, ma non lo è.

La soluzione è utilizzare i metodi Arrays.copyOf... in Java 6 e versioni successive oppure, se si utilizza una versione precedente di Java, utilizzare Reflection per creare il tipo corretto di array. Ad esempio,

T [] risultato = (T []) Array.newInstance (array.getClass().getComponentType(), dimensione);

2 Devo scrivere metodi separati per gestire gli array primitivi oppure è una soluzione migliore?

Probabilmente è meglio scrivere metodi separati. In Java, le matrici di tipi primitivi sono completamente separate da matrici di tipi di riferimento; e non c'è un buon modo di lavorare con entrambi.

È possibile utilizzare Reflection per affrontare entrambi allo stesso tempo. Reflection ha i metodi Array.get() e Array.set() che funzionano allo stesso modo su array primitivi e array di riferimento. Tuttavia, si perde la sicurezza del tipo in questo modo poiché l'unico supertipo sia di matrici primitive sia di matrici di riferimento è Object.

1

Java non permette la costruzione di array generici in modo type-safe. Utilizzare invece un tipo di sequenza generica (ad esempio java.util.List, ad esempio).

Ecco come vorrei scrivere il programma di test, utilizzando una classe contenitore generico fj.data.Stream:

import fj.data.Stream; 
import static fj.data.Stream.range; 

// ... 

public int[] intArray(Stream<Integer> s) { 
    return s.toArray(Integer.class).array() 
} 

public static void main(String[] args) { 
    Stream<Integer> integerStream = range(1, 10); 
    print(intArray(integerStream)); 
    print(intArray(integerStream.take(3))); 
    print(intArray(integerStream.drop(3))); 

    // ... 
} 
0

Hai due problemi con quello che si sta cercando di realizzare.

Prima di tutto, si sta tentando di utilizzare i tipi primitivi, che in realtà non ereditano da Object. Questo rovinerà le cose. Se davvero bisogno di fare questo, utilizzare in modo esplicito Integer piuttosto che int, ecc

Il secondo e più grande problema è che i generici Java hanno tipo di cancellazione. Ciò significa che in fase di runtime non è possibile fare effettivamente riferimento al tipo di generico. Questo è stato fatto per permetterti di mescolare il codice di supporto generico e non generico, e finì per (IMHO) essere una fonte importante di mal di testa per gli sviluppatori Java e un'altra prova che i generici avrebbero dovuto essere in Java dal primo giorno. Ti suggerisco leggere la parte nel tutorial a riguardo, renderà più chiaro questo problema.

0

Probabilmente si dovrà per avvolgere le primitive in collezioni corrispondenti.

Inoltre suggerisco di dare un'occhiata a Trove collezioni primitivi (http://trove.starlight-systems.com). Questo non è correlato alla tua domanda sui generici, ma potrebbe essere piuttosto interessante.

Problemi correlati