web-dev-qa-db-fra.com

Méthode statique dans une classe générique?

En Java, j'aimerais avoir quelque chose comme:

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

Mais je reçois

Impossible de faire une référence statique au type non statique T

Je ne comprends pas les génériques au-delà des utilisations de base et je ne peux donc pas en comprendre le sens. Cela n'aide pas que je n'ai pas pu trouver beaucoup d'informations sur Internet sur le sujet.

Quelqu'un pourrait-il préciser si une telle utilisation est possible, de manière similaire? Aussi, pourquoi ma tentative initiale a-t-elle échoué?

177
André Chalella

Vous ne pouvez pas utiliser les paramètres de type générique d'une classe dans des méthodes ou des champs statiques. Les paramètres de type de la classe ne concernent que les méthodes d'instance et les champs d'instance. Pour les champs et les méthodes statiques, ils sont partagés par toutes les instances de la classe, même par des instances de paramètres de type différents. Par conséquent, elles ne peuvent évidemment pas dépendre d'un paramètre de type particulier.

Il ne semble pas que votre problème doive nécessiter l’utilisation du paramètre type de la classe. Si vous décrivez plus en détail ce que vous essayez de faire, nous pourrons peut-être vous aider à trouver un meilleur moyen de le faire.

244
newacct

Java ne sait pas ce que T est jusqu'à ce que vous instanciez un type.

Peut-être que vous pouvez exécuter des méthodes statiques en appelant Clazz<T>.doit(something), mais il semble que vous ne puissiez pas.

L'autre façon de gérer les choses est de mettre le paramètre type dans la méthode elle-même:

static <U> void doIt(U object)

ce qui ne vous donne pas la bonne restriction sur U, mais c'est mieux que rien ....

126
Jason S

J'ai rencontré ce même problème. J'ai trouvé ma réponse en téléchargeant le code source de Collections.sort dans le framework Java. La réponse que j'ai utilisée était de mettre le <T> generic dans la méthode, pas dans la définition de la classe.

Donc cela a fonctionné:

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

}

Bien sûr, après avoir lu les réponses ci-dessus, je me suis rendu compte que ce serait une alternative acceptable sans utiliser une classe générique:

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

Il est possible de faire ce que vous voulez en utilisant la syntaxe des méthodes génériques lors de la déclaration de votre méthode doIt() (notez l'ajout de <T> entre static et void dans la signature de la méthode doIt()):

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

J'ai demandé à l'éditeur Eclipse d'accepter le code ci-dessus sans l'erreur Cannot make a static reference to the non-static type T, puis de l'étendre au programme de travail suivant (avec une référence culturelle quelque peu appropriée à l'âge):

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);
  }
}

Qui affiche ces lignes sur la console lorsque je l’exécute:

secouez ce butin 'classe com.eclipseoptions.datamanager.Clazz $ KC' !!!
secouez ce butin 'classe com.eclipseoptions.datamanager.Clazz $ SunshineBand' !!!

13
BD at Rivenhill

Je pense que cette syntaxe n'a pas encore été mentionnée (dans le cas où vous voulez une méthode sans arguments):

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

Et l'appel:

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

J'espère que cela aidera quelqu'un.

6
Oreste Viron

Il est correctement mentionné dans l'erreur: vous ne pouvez pas faire de référence statique au type T. non statique. La raison en est que le paramètre de type T peut être remplacé par n'importe lequel des arguments de type, par exemple. Clazz<String> ou Clazz<integer> etc. Mais les champs/méthodes statiques sont partagés par tous les objets non statiques de la classe. 

L'extrait suivant est extrait du doc :

Le champ statique d'une classe est une variable de niveau de classe partagée par tous objets non statiques de la classe. Par conséquent, les champs statiques de type les paramètres ne sont pas autorisés. Considérez la classe suivante:

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

    // ...
}

Si les champs statiques de type paramètres étaient autorisés, le code suivant serait confondu:

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

Comme le champ os statique est partagé par le téléphone, le pageur et le PC, quel est le type réel de os? Il ne peut s'agir ni d'un smartphone, ni d'un téléavertisseur, ni du TabletPC en même temps. Vous ne pouvez donc pas créer de champs statiques de type paramètres.

Comme le souligne à juste titre Chris dans son answer , vous devez utiliser le paramètre type avec la méthode et non avec la classe dans ce cas. Vous pouvez l'écrire comme ceci:

static <E> void doIt(E object) 
6
i_am_zero

D'autres ont déjà répondu à votre question, mais je peux également vous recommander le livre O'Reilly Java Generics . C'est parfois un sujet subtil et complexe, et si cela semble souvent poser des restrictions inutiles, mais le livre explique fort bien pourquoi les génériques Java sont ce qu'ils sont.

4
skaffman

Quelque chose comme ce qui suit vous rapprocherait

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

EDIT: Exemple mis à jour avec plus de détails

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());
    }
}
3
ekj

Lorsque vous spécifiez un type générique pour votre classe, la machine virtuelle Java ne le sait que si elle possède une instance de votre classe et non une définition. Chaque définition a uniquement un type paramétré.

Les génériques fonctionnent comme des modèles en C++. Vous devez donc instancier votre classe, puis utiliser la fonction avec le type spécifié.

2
Marcin Cylke

@BD at Rivenhill: Puisque cette question a suscité un regain d’attention l’année dernière, parlons un peu, juste pour discuter ... Le corps de votre méthode doIt ne fait rien de spécifique à T. C'est ici:

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

Vous pouvez donc entièrement supprimer toutes les variables de type et simplement coder

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

D'accord. Mais revenons plus près du problème initial. La première variable de type de la déclaration de classe est redondante. Seul le second sur la méthode est nécessaire. Nous y revoilà, mais ce n'est pas la réponse finale pour le moment: 

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";
      }
}

Cependant, il n’ya que peu de problèmes avec rien, puisque la version suivante fonctionne de la même manière. Tout ce dont il a besoin est le type d'interface sur le paramètre method. Aucune variable de type en vue nulle part. Était-ce vraiment le problème initial?

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";
      }
}
1
Hubert Kauker

En termes simples, cela se produit en raison de la propriété "Erasure" des génériques. Cela signifie que, bien que nous définissions ArrayList<Integer> et ArrayList<String>, au moment de la compilation, il reste sous forme de deux types concrets différents, tape et crée une seule classe ArrayList au lieu de deux classes. Ainsi, lorsque nous définissons une méthode de type statique ou quoi que ce soit pour un générique, il est partagé par toutes les instances de ce générique. Dans mon exemple, il est partagé à la fois par ArrayList<Integer> et ArrayList<String>. N'est pas autorisé dans un contexte statique! 

1
user6288471

Puisque les variables statiques sont partagées par toutes les instances de la classe. Par exemple, si vous rencontrez le code suivant

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

T est disponible uniquement après la création d'une instance. Mais les méthodes statiques peuvent être utilisées avant même que des instances ne soient disponibles. Ainsi, les paramètres de type générique ne peuvent pas être référencés dans des méthodes et des variables statiques

0
Bharat