74 votes

Instancier un type générique en java

Je voudrais créer un objet de type générique en java. Veuillez me suggérer comment je peux y parvenir.

Note : Ceci peut sembler être un problème générique trivial. Mais je parie qu'il ne l'est pas. :)

supposons que j'aie la déclaration de classe comme :

public class Abc<T> {
    public T getInstanceOfT() {
       // I want to create an instance of T and return the same.
    }
}

0 votes

Comment faites-vous pour savoir ce qu'est le "T" ? Quelle partie du programme vous "dit" quel T utiliser ?

1 votes

Je vous suggère également la FAQ sur les produits génériques : angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html

81voto

Jens Schauder Points 23468
public class Abc<T> {
    public T getInstanceOfT(Class<T> aClass) {
       return aClass.newInstance();
    }
}

Vous devrez ajouter la gestion des exceptions.

Vous devez transmettre le type réel au moment de l'exécution, car il ne fait pas partie du code octet après la compilation, et il n'y a donc aucun moyen de le connaître sans le fournir explicitement.

8 votes

Comment utilisez-vous cette méthode ? En d'autres termes, à quoi ressemblerait un appel ? T newT = this.getInstance(T.class) ne compilera pas.

0 votes

aClass.newInstance();

5 votes

clazz.newInstance() est déprécié à partir de Java 9 : peut être remplacé par clazz.getDeclaredConstructor().newInstance() .

28voto

Martin Points 13951

Dans le code que vous avez posté, il est impossible de créer une instance de T puisque vous ne savez pas de quel type il s'agit :

public class Abc<T>
{
       public T getInstanceOfT()
       {
           // There is no way to create an instance of T here
           // since we don't know its type
       }
} 

Bien sûr, c'est possible si vous avez une référence à Class<T> y T a un constructeur par défaut, il suffit d'appeler newInstance() sur le Class objet.

Si vous sous-classez Abc<T> vous pouvez même contourner le problème de l'effacement des types et vous n'aurez pas à passer d'informations. Class<T> références autour :

import java.lang.reflect.ParameterizedType;

public class Abc<T>
{
    T getInstanceOfT()
    {
        ParameterizedType superClass = (ParameterizedType) getClass().getGenericSuperclass();
        Class<T> type = (Class<T>) superClass.getActualTypeArguments()[0];
        try
        {
            return type.newInstance();
        }
        catch (Exception e)
        {
            // Oops, no default constructor
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args)
    {
        String instance = new SubClass().getInstanceOfT();
        System.out.println(instance.getClass());
    }
}

class SubClass
    extends Abc<String>
{
}

0 votes

Votre raison est fausse puisque la même chose pourrait être dite de Class<T> Pourtant, nous sommes ici puede créer une instance.

0 votes

Je ne suis pas d'accord avec vous. J'ai été en mesure de réaliser la même chose vérifiez ma réponse.

0 votes

Bien, bien sûr, il est possible d'instancier T si vous connaissez son type (par Class<T> ) et ce type a un constructeur par défaut. Ce n'était cependant pas le cas dans le code initialement publié.

15voto

Jack Points 61503

Ce que tu as écrit n'a aucun sens, génériques en Java sont destinés à ajouter la fonctionnalité de polymorphisme paramétrique aux objets.

Qu'est-ce que cela signifie ? Ça veut dire que tu veux garder un peu variables de type de vos classes indécises, pour pouvoir utiliser vos classes avec de nombreux types différents.

Mais votre variable de type T est un attribut qui est résolu au moment de l'exécution, le compilateur Java compilera votre classe en prouvant la sécurité de type sans essayer de savoir quel type d'objet est T il est donc impossible qu'il vous laisse utiliser une variable de type dans une méthode statique. Le type est associé à une instance d'exécution de l'objet tandis que le type est associé à une instance d'exécution de l'objet. public void static main(..) est associé à la définition de la classe et à cette portée T ne veut rien dire.

Si vous voulez utiliser un variable de type à l'intérieur d'une méthode statique, vous devez déclarer la méthode comme générique (ceci parce que, comme expliqué variables de type d'une classe modèle sont liées à son instance d'exécution ), pas la classe :

class SandBox
{
  public static <T> void myMethod()
  {
     T foobar;
  }
}

cela fonctionne, mais bien sûr pas avec main puisqu'il n'y a aucun moyen de l'appeler de manière générique.

EDITAR : Le problème est qu'à cause de effacement de type une seule classe générique est compilée et transmise à la JVM. Le vérificateur de type vérifie simplement si le code est sûr, puis, comme il l'a prouvé, chaque type de information générique est rejetée.

Pour instancier T vous devez connaître le type de T mais il peut s'agir de plusieurs types en même temps. Une solution qui ne nécessite qu'un minimum de réflexion consiste donc à utiliser Class<T> pour instancier de nouveaux objets :

public class SandBox<T>
{
    Class<T> reference;

    SandBox(Class<T> classRef)
    {
        reference = classRef;
    }

    public T getNewInstance()
    {
        try
        {
            return reference.newInstance();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        return null;
    }

    public static void main(String[] args)
    {
        SandBox<String> t = new SandBox<String>(String.class);

        System.out.println(t.getNewInstance().getClass().getName());
    }
}

Bien sûr, cela implique que le type que vous voulez instancier :

  • n'est pas un type primitif
  • il a un constructeur par défaut

Pour utiliser d'autres types de constructeurs, vous devez approfondir la réflexion.

3 votes

C'est parfaitement logique en .NET, où les génériques sont conceptuellement très similaires à ceux de Java. La seule véritable différence est que .NET ne dispose pas de l'effacement de type et c'est la technique différence qui fait qu'un tel code fonctionne en .NET et pas en Java.

0 votes

Ne tenez pas compte du fait qu'il est appelé depuis une méthode principale. J'ai mis à jour le code. Maintenant, nous avons une méthode getInstanceOfT.

2 votes

Konrad, .NET dispose de l'inférence de type, il sait de quoi vous parlez même si vous ne l'indiquez pas explicitement, par exemple, de nombreuses fonctions de IEnumerable sont des fonctions de type générique et pourtant vous n'avez souvent pas besoin de spécifier l'opérateur diamant parce que .NET infère son type générique. J'aimerais vraiment que Java soit aussi intelligent. En outre, dans .NET, vous pouvez simplement appeler default(T); o Activator.CreateInstance(typeof(T));

5voto

BalusC Points 498232

Le seul moyen de le faire fonctionner est d'utiliser Génériques réifiés . Et cela n'est pas pris en charge par Java (pas encore ? il l'était prévu pour Java 7, mais a été repoussé). En C# par exemple, il est supporté en supposant que T a un par défaut constructeur. Vous pouvez même obtenir le type d'exécution en typeof(T) et obtenir les constructeurs par Type.GetConstructor() . Je ne fais pas de C# donc la syntaxe peut être invalide, mais cela ressemble grossièrement à ceci :

public class Foo<T> where T:new() {
    public void foo() {
        T t = new T();
    }
}

La meilleure solution de contournement en Java est de passer un fichier Class<T> comme argument de méthode à la place, comme plusieurs réponses l'ont déjà souligné.

0 votes

Est-ce que l'ajout de Génériques réifiés à Java fermé ? bugs.java.com/bugdatabase/view_bug.do?bug_id=8061418

1voto

Tedil Points 1404

Vous ne semblez pas comprendre le fonctionnement des génériques. Vous devriez peut-être regarder http://java.sun.com/j2se/1.5.0/docs/guide/language/generics.html En gros, ce que vous pourrait est quelque chose comme

public class Abc<T>
{
  T someGenericThing;
  public Abc(){}
  public T getSomeGenericThing()
  {
     return someGenericThing;
  }

  public static void main(String[] args)
  {
     // create an instance of "Abc of String"
        Abc<String> stringAbc = new Abc<String>();
        String test = stringAbc.getSomeGenericThing();
  }

}

11 votes

+1 Vous avez parfaitement exposé le problème. La seule chose qui manque est... Où est-ce que nous initialisons someGenericThing ?

0 votes

D'accord avec Mohd, stringAbc.getSomeGenericThing() est en fait nul.

0 votes

Oui, j'ai oublié d'ajouter cette partie. Vous devriez bien sûr définir/initialiser certains T que vous souhaitez, par exemple dans le constructeur d'Abc. Ou via les méthodes Setter. Mais le problème de Mohd ne concerne pas ce que j'ai posté de toute façon (du moins après qu'il ait posté des commentaires).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X