Come rendere statico un metodo generico Java?


173

Di seguito è riportato uno snippet su come creare una classe generica java per aggiungere un singolo elemento a un array. Come posso rendere appendToArray un metodo statico. L'aggiunta di statica alla firma del metodo provoca errori di compilazione.

public class ArrayUtils<E> {

        public E[] appendToArray(E[] array, E item) {
            E[] result = (E[])new Object[array.length+1];
            result[array.length] = item;
            return result;
        }
}

Quali errori di compilazione ricevi? Inoltre, perché non usare solo uno dei contenitori di libreria standard?
Karl Knechtel,

1
Errore di compilazione: in realtà stavo aggiungendo un modificatore statico errato. Uso delle raccolte: Sì, l'utilizzo di una raccolta sarebbe l'ideale, ma la domanda non riguarda le raccolte rispetto a quelle dell'array, il mio caso d'uso richiede un array.
Chris Johnson,

Nota che dovrai usare la riflessione (EVIL) per impedire al codice client di generare un'eccezione in alcune, ma non in tutte le circostanze (bello). È meglio evitare le matrici di riferimento.
Tom Hawtin - tackline il

Risposte:


283

l'unica cosa che puoi fare è cambiare la tua firma in

public static <E> E[] appendToArray(E[] array, E item)

Dettagli importanti:

Le espressioni generiche che precedono il valore restituito introducono sempre (dichiarano) una nuova variabile di tipo generico.

Inoltre, digitare le variabili tra i tipi ( ArrayUtils) e i metodi statici ( appendToArray) non interferiscono mai tra loro.

Allora, che cosa significa questo: Nella mia risposta <E>sarebbe nascondere il Eda ArrayUtils<E>se il metodo non sarebbe static. E <E>non ha nulla a che fare con il Eda ArrayUtils<E>.

Per riflettere meglio questo fatto, una risposta più corretta sarebbe:

public static <I> I[] appendToArray(I[] array, I item)

30
Si noti inoltre che non esiste assolutamente alcuna relazione tra la variabile di tipo a livello di classe Ee la variabile di tipo di metodo statico E. Ritengo sia molto meglio usare un nome di variabile diverso quando si dichiarano metodi generici, statici o meno, all'interno di classi generiche.
Giudice Mental

ma in questo caso posso passare un oggetto di tipo diverso nei parametri. Come se potessi passare l'array Integer [] come primo parametro e doppio oggetto.
pinkpanther,

pinkpanther: vero, ma non fa alcun danno, perché il metodo statico funziona sempre e solo su un oggetto array che gli viene passato tramite un parametro, quindi i suoi elementi avranno sicuramente il tipo corretto.
Dabbler,

80
public static <E> E[] appendToArray(E[] array, E item) { ...

Nota il <E>.

I metodi generici statici richiedono una propria dichiarazione generica ( public static <E>) separata dalla dichiarazione generica della classe ( public class ArrayUtils<E>).

Se il compilatore si lamenta di un'ambiguità di tipo nell'invocare un metodo generico statico (di nuovo non probabile nel tuo caso, ma, in generale, nel caso), ecco come invocare esplicitamente un metodo generico statico utilizzando un tipo specifico ( _class_.<_generictypeparams_>_methodname_):

String[] newStrings = ArrayUtils.<String>appendToArray(strings, "another string");

Ciò accadrebbe solo se il compilatore non fosse in grado di determinare il tipo generico perché, ad esempio, il tipo generico non è correlato agli argomenti del metodo.


10

Devi spostare il parametro type sul livello del metodo per indicare che hai un metodo generico anziché una classe generica:

public class ArrayUtils {
    public static <T> E[] appendToArray(E[] array, E item) {
        E[] result = (E[])new Object[array.length+1];
        result[array.length] = item;
        return result;
    }
}

1
Ciò non funzionerà perché non è stato definito il tipo generico E. In questo caso, nella definizione della classe è ancora necessario avere il tipo generico <E>.
George Xavier,

0

Lo spiego in modo semplice.

I generici definiti a livello di classe sono completamente separati dai generici definiti a livello di metodo (statico).

class Greet<T> {

    public static <T> void sayHello(T obj) {
        System.out.println("Hello " + obj);
    }
}

Quando vedi il codice sopra riportato ovunque, tieni presente che la T definita a livello di classe non ha nulla a che fare con la T definita nel metodo statico. Anche il seguente codice è completamente valido ed equivalente al codice sopra.

class Greet<T> {

    public static <E> void sayHello(E obj) {
        System.out.println("Hello " + obj);
    }
}

Perché il metodo statico deve avere i propri generici separati da quelli della classe?

Questo perché, il metodo statico può essere chiamato senza nemmeno creare un'istanza della classe. Quindi, se la Classe non è ancora istanziata, non sappiamo ancora cosa sia T. Questo è il motivo per cui i metodi statici devono avere i propri generici.

Quindi, ogni volta che chiami il metodo statico,

Greet.sayHello("Bob");
Greet.sayHello(123);

JVM lo interpreta come segue.

Greet.<String>sayHello("Bob");
Greet.<Integer>sayHello(123);

Entrambi danno gli stessi risultati.

Hello Bob
Hello 123
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.