Metodo statico in una classe generica?


197

In Java, vorrei avere qualcosa come:

class Clazz<T> {
  static void doIt(T object) {
    // ...
  }
}

Ma capisco

Impossibile fare un riferimento statico al tipo T non statico

Non capisco i generici oltre gli usi di base e quindi non ho molto senso. Non aiuta a non riuscire a trovare molte informazioni su Internet sull'argomento.

Qualcuno potrebbe chiarire se tale uso è possibile, in modo simile? Inoltre, perché il mio tentativo originale non ha avuto successo?

Risposte:


270

Non è possibile utilizzare i parametri di tipo generico di una classe in metodi statici o campi statici. I parametri di tipo della classe sono solo nell'ambito dei metodi di istanza e dei campi di istanza. Per i campi statici e i metodi statici, sono condivisi tra tutte le istanze della classe, anche le istanze con parametri di tipo diverso, quindi ovviamente non possono dipendere da un particolare parametro di tipo.

Non sembra che il tuo problema debba richiedere l'utilizzo del parametro type della classe. Se descrivi ciò che stai cercando di fare in modo più dettagliato, forse possiamo aiutarti a trovare un modo migliore per farlo.


9
Sottoposta a votazione, questa risposta in realtà spiega il problema del poster invece di fornire solo una soluzione alternativa.
Jorn,

7
@Andre: il tuo intuito non è infondato; C # tratta davvero i generici in questo modo.
jyoungdev,

32
"Per i campi statici e i metodi statici, sono condivisi tra tutte le istanze della classe, anche istanze con parametri di tipo diverso ..." Ahi! Calciato di nuovo con i dadi per tipo di cancellazione!
BD a Rivenhill,

2
se osserverai come appaiono la classe / i metodi generici dopo la compilazione, vedrai che l'attributo generico viene rimosso. E Lista <Integer> dopo la compilazione assomiglia a "Elenco". Quindi non c'è differenza tra Elenco <Integer> e Elenco <Lungo> dopo la compilazione - entrambi sono diventati Elenco.
Dainius,

3
Penso che abbia spiegato cosa sta cercando di fare abbastanza bene. È chiaro che sta cercando di scuotere il bottino! hah
astryk,

140

Java non sa cos'è Tfino a quando non si crea un'istanza di un tipo.

Forse puoi eseguire metodi statici chiamando Clazz<T>.doit(something)ma sembra che non puoi.

L'altro modo di gestire le cose è inserire il parametro type nel metodo stesso:

static <U> void doIt(U object)

che non ti dà la giusta restrizione su U, ma è meglio di niente ....


Quindi chiamerei il metodo senza specificare alcun vincolo, ovvero Clazz.doIt (oggetto) invece di Clazz <Object> .doIt (oggetto), giusto? Lo consideri OK?
André Chalella,

La seconda sintassi è quella più esatta, che è necessaria se il compilatore non può inferire il tipo del valore restituito dal contaxt in cui viene chiamato il metodo. In altre parole, se il compilatore consente Clazz.doIt (oggetto), quindi farlo.
Skaffman,

1
Ho provato Clazz <Object> .doIt (oggetto) e ho avuto un errore di compilazione! "Errore di sintassi su token (s), costrutti fuori posto". Clazz.doIt (oggetto) funziona benissimo, nemmeno un avvertimento.
André Chalella,

8
Usa Clazz. <Object> doIt (oggetto). Penso che sia una strana sintassi, rispetto al Clazz di C ++ <int> :: doIt (1)
Crend King

Perché dici che non ti dà la giusta restrizione su U?
Adam Burley,

46

Ho riscontrato questo stesso problema. Ho trovato la mia risposta scaricando il codice sorgente Collections.sortnel framework java. La risposta che ho usato è stata quella di inserire il <T>generico nel metodo, non nella definizione della classe.

Quindi ha funzionato:

public class QuickSortArray  {
    public static <T extends Comparable> void quickSort(T[] array, int bottom, int top){
//do it
}

}

Naturalmente, dopo aver letto le risposte sopra ho capito che questa sarebbe un'alternativa accettabile senza usare una classe generica:

public static void quickSort(Comparable[] array, int bottom, int top){
//do it
}

8
Mentre la risposta accettata era tecnicamente corretta: questo è proprio quello che stavo cercando quando Google mi ha portato a questa domanda.
Jeremy List,

Puntelli per fornire un esempio che include la T extends XXXsintassi.
Ogre Salmo33,

3
@Chris Dato che stai usando comunque i generici, potresti anche usarli fino in fondo --- vale a dire non usare tipi grezzi come Comparable. Prova <T extends Comparable<? super T>>invece.
easoncxz,

15

È possibile fare ciò che si desidera utilizzando la sintassi per i metodi generici quando si dichiara il doIt()metodo (notare l'aggiunta di <T>tra statice voidnella firma del metodo di doIt()):

class Clazz<T> {
  static <T> void doIt(T object) {
    // shake that booty
  }
}

Ho ottenuto l'editor di Eclipse per accettare il codice sopra senza l' Cannot make a static reference to the non-static type Terrore e poi l'ho esteso al seguente programma di lavoro (completo di riferimento culturale in qualche modo adatto all'età):

public class Clazz<T> {
  static <T> void doIt(T object) {
    System.out.println("shake that booty '" + object.getClass().toString()
                       + "' !!!");
  }

  private static class KC {
  }

  private static class SunshineBand {
  }

  public static void main(String args[]) {
    KC kc = new KC();
    SunshineBand sunshineBand = new SunshineBand();
    Clazz.doIt(kc);
    Clazz.doIt(sunshineBand);
  }
}

Che stampa queste righe sulla console quando lo eseguo:

scuoti quel bottino 'class com.eclipseoptions.datamanager.Clazz $ KC' !!!
scuoti quel bottino 'class com.eclipseoptions.datamanager.Clazz $ SunshineBand' !!!


In questo caso, il secondo <T> nasconde il primo?
André Chalella,

1
@ AndréNeves, Sì. Il secondo <T>maschera il primo allo stesso modo che nel class C { int x; C(int x) { ... } }parametro xmaschera il campo x.
Mike Samuel,

Come spiegare l'output del campione: sembra che siano effettivamente coinvolti due tipi, uno per parametro T valore? Se T stesse davvero nascondendo il tipo di classe, mi aspetterei che "scuoti quel bottino" com.eclipseoptions.datamanager.Clazz "emesso due volte senza parametri.
Pragmateek,

1
Non ha nulla a che fare con il mascheramento. Un metodo statico semplicemente non può essere associato al tipo generico di classi . Tuttavia può definire i propri tipi e confini generici.
mike

C'è un modo per definire il tipo statico una volta e riutilizzarlo per diversi metodi statici? Non sono un fan del fare static <E extends SomeClass> E foo(E input){return input;}per ogni singolo metodo quando vorrei fare qualcosa del generestatic <E extends SomeClass>; //static generic type defined only once and reused static E foo(E input){return input;} static E bar(E input){return input;} //...etc...
HesNotTheStig

14

Penso che questa sintassi non sia stata ancora menzionata (nel caso in cui si desideri un metodo senza argomenti):

class Clazz {
  static <T> T doIt() {
    // shake that booty
  }
}

E la chiamata:

String str = Clazz.<String>doIt();

Spero che questo aiuti qualcuno.


1
In realtà (non conosco la versione Java), questo è possibile anche senza il <String>perché infonde semplicemente l'argomento type perché lo assegni a una variabile. Se non lo assegni, si deduce semplicemente Object.
Adowrath,

Questa è una soluzione perfetta
Prabhat Ranjan,

6

È correttamente citato nell'errore: non è possibile fare un riferimento statico al tipo non statico T. Il motivo è che il parametro type Tpuò essere sostituito da qualsiasi argomento tipo eg Clazz<String>o Clazz<integer>ecc. Ma i campi / metodi statici sono condivisi da tutti i non oggetti statici della classe.

Il seguente estratto è tratto dal documento :

Il campo statico di una classe è una variabile a livello di classe condivisa da tutti gli oggetti non statici della classe. Pertanto, i campi statici con parametri di tipo non sono consentiti. Considera la seguente classe:

public class MobileDevice<T> {
    private static T os;

    // ...
}

Se fossero consentiti campi statici con parametri di tipo, il seguente codice verrebbe confuso:

MobileDevice<Smartphone> phone = new MobileDevice<>();
MobileDevice<Pager> pager = new MobileDevice<>();
MobileDevice<TabletPC> pc = new MobileDevice<>();

Poiché il sistema operativo statico è condiviso da telefono, cercapersone e PC, qual è il tipo di sistema operativo attuale? Non può essere contemporaneamente Smartphone, Pager e TabletPC. Pertanto, non è possibile creare campi statici di parametri di tipo.

Come giustamente sottolineato da chris nella sua risposta, in questo caso è necessario utilizzare il parametro type con il metodo e non con la classe. Puoi scriverlo come:

static <E> void doIt(E object) 

3

Qualcosa di simile al seguente ti avvicinerebbe

class Clazz
{
   public static <U extends Clazz> void doIt(U thing)
   {
   }
}

EDIT: esempio aggiornato con maggiori dettagli

public abstract class Thingo 
{

    public static <U extends Thingo> void doIt(U p_thingo)
    {
        p_thingo.thing();
    }

    protected abstract void thing();

}

class SubThingoOne extends Thingo
{
    @Override
    protected void thing() 
    {
        System.out.println("SubThingoOne");
    }
}

class SubThingoTwo extends Thingo
{

    @Override
    protected void thing() 
    {
        System.out.println("SuThingoTwo");
    }

}

public class ThingoTest 
{

    @Test
    public void test() 
    {
        Thingo t1 = new SubThingoOne();
        Thingo t2 = new SubThingoTwo();

        Thingo.doIt(t1);
        Thingo.doIt(t2);

        // compile error -->  Thingo.doIt(new Object());
    }
}

Esattamente come suggerito da Jason S.
André Chalella,

@Andre il precedente suggerimento non prevedeva la limitazione secondo cui U deve estendere Clazz
ekj

Vedo, ma non aggiunge nulla di utile tranne il vincolo. Nel mio post originale, vorrei poterlo fare senza passare U come parametro.
André Chalella,

@Andre Vedi il mio aggiornamento sopra. Il tipo generico è definito solo nella definizione del metodo e non deve essere passato quando si chiama il metodo
ekj

@ekj Grazie, adesso capisco. Mi dispiace, ho posto questa domanda tre anni fa, quando facevo Java ogni giorno. Ho avuto la tua idea, anche se penso che tu capisca che questo non è esattamente quello che intendevo inizialmente. Tuttavia, mi è piaciuta la tua risposta.
André Chalella,

2

Quando specifichi un tipo generico per la tua classe, JVM sa che ha solo un'istanza della tua classe, non la definizione. Ogni definizione ha solo un tipo parametrizzato.

I generici funzionano come modelli in C ++, quindi è necessario prima creare un'istanza della classe, quindi utilizzare la funzione con il tipo specificato.


2
I generici Java sono abbastanza diversi dai modelli C ++. La classe generica è compilata da sola. In effetti il ​​codice equivalente in C ++ funzionerebbe (il codice chiamante sarebbe simile Clazz<int>::doIt( 5 ))
David Rodríguez - dribeas,

Mi piace questa risposta meglio di quella accettata ... a parte il riferimento al modello C ++, il commento sul tipo generico è solo per un'istanza della classe invece dell'intera classe è perfetto. La risposta accettata non spiega questo e fornisce solo una soluzione alternativa che non ha nulla a che fare con il motivo per cui non è possibile utilizzare il tipo generico della classe in un metodo statico.
Jorn,

1

Per dirla in parole povere, succede a causa della proprietà "Erasure" dei generici. Ciò significa che sebbene noi definiamo ArrayList<Integer>e ArrayList<String>, al momento della compilazione, rimane come due diversi tipi concreti ma in fase di esecuzione la JVM cancella i tipi generici e crea solo una classe ArrayList invece di due classi. Quindi quando definiamo un metodo di tipo statico o altro per un generico, è condiviso da tutte le istanze di quel generico, nel mio esempio è condiviso da entrambi ArrayList<Integer>e ArrayList<String>. Ecco perché si ottiene l'errore. Un parametro di tipo generico di una classe non è Consentito in un contesto statico!


1

@BD a Rivenhill: da quando questa vecchia domanda ha ottenuto rinnovata attenzione lo scorso anno, continuiamo un po ', solo per motivi di discussione. Il corpo del tuo doItmetodo non fa nulla di Tspecifico. Ecco qui:

public class Clazz<T> {
  static <T> void doIt(T object) {
    System.out.println("shake that booty '" + object.getClass().toString()
                       + "' !!!");
  }
// ...
}

Quindi puoi eliminare completamente tutte le variabili di tipo e solo il codice

public class Clazz {
  static void doIt(Object object) {
    System.out.println("shake that booty '" + object.getClass().toString()
                       + "' !!!");
  }
// ...
}

Ok. Ma torniamo più vicini al problema originale. La prima variabile di tipo nella dichiarazione di classe è ridondante. È necessario solo il secondo sul metodo. Eccoci di nuovo, ma non è ancora la risposta finale:

public class Clazz  {
  static <T extends Saying> void doIt(T object) {
    System.out.println("shake that booty "+ object.say());
  }

  public static void main(String args[]) {
    Clazz.doIt(new KC());
    Clazz.doIt(new SunshineBand());
  }
}
// Output:
// KC
// Sunshine

interface Saying {
      public String say();
}

class KC implements Saying {
      public String say() {
          return "KC";
      }
}

class SunshineBand implements Saying {
      public String say() {
          return "Sunshine";
      }
}

Tuttavia, tutto è troppo complicato per nulla, poiché la seguente versione funziona allo stesso modo. Tutto ciò che serve è il tipo di interfaccia sul parametro del metodo. Nessuna variabile di tipo in vista da nessuna parte. Era davvero il problema originale?

public class Clazz  {
  static void doIt(Saying object) {
    System.out.println("shake that booty "+ object.say());
  }

  public static void main(String args[]) {
    Clazz.doIt(new KC());
    Clazz.doIt(new SunshineBand());
  }
}

interface Saying {
      public String say();
}

class KC implements Saying {
      public String say() {
          return "KC";
      }
}

class SunshineBand implements Saying {
      public String say() {
          return "Sunshine";
      }
}

0

Poiché le variabili statiche sono condivise da tutte le istanze della classe. Ad esempio se hai il seguente codice

class Class<T> {
  static void doIt(T object) {
    // using T here 
  }
}

T è disponibile solo dopo la creazione di un'istanza. Tuttavia, è possibile utilizzare metodi statici anche prima che siano disponibili le istanze. Pertanto, non è possibile fare riferimento a parametri di tipo generico all'interno di metodi e variabili statici

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.