159 votes

Instancier une classe générique en Java

Je sais que les génériques de Java sont quelque peu inférieurs à ceux de .Net.

J'ai une classe générique Foo<T> , et j'ai vraiment besoin d'instancier un T en Foo utilisant un constructeur sans paramètre. Comment peut-on contourner la limitation de Java?

177voto

Jon Skeet Points 692016

Une option consiste à transmettre Bar.class (ou n'importe quel type qui vous intéresse - toute manière de spécifier la référence appropriée Class<T> ) et à conserver cette valeur en tant que champ:

 public class Test
{   
    public static void main(String [] args)
        throws Exception // Just for simplicity!
    {
        Generic<Bar> x = new Generic<Bar>(Bar.class);
        Bar y = x.buildOne();
    }
}

public class Generic<T>
{
    private Class<T> clazz;

    public Generic(Class<T> clazz)
    {
        this.clazz = clazz;
    }

    public T buildOne() throws InstantiationException,
        IllegalAccessException
    {
        return clazz.newInstance();
    }
}

public class Bar
{
    public Bar()
    {
        System.out.println("Constructing");
    }
}
 

Une autre option consiste à avoir une interface "factory" et à transmettre une fabrique au constructeur de la classe générique. C'est plus flexible et vous n'avez pas à vous soucier des exceptions de réflexion.

49voto

Comptrol Points 4415

Et c'est l'implémentation de Factory, comme Jon Skeet l'a suggéré :

 interface Factory<T> {
    T factory();
}

class Araba {
    //static inner class for Factory<T> implementation
    public static class ArabaFactory implements Factory<Araba> {
        public Araba factory() {
            return new Araba();
        }
    }
    public String toString() { return "Abubeee"; }
}

class Generic<T> {
    private T var;

    Generic(Factory<T> fact) {
        System.out.println("Constructor with Factory<T> parameter");
        var = fact.factory();
    }
    Generic(T var) {
        System.out.println("Constructor with T parameter");
        this.var = var;
    }
    T get() { return var; }
}

public class Main {
    public static void main(String[] string) {
        Generic<Araba> gen = new Generic<Araba>(new Araba.ArabaFactory());
        System.out.print(gen.get());
    }
}
 

Sortie:

 Constructor with Factory<T> parameter
Abubeee
 

7voto

Glenn Points 3343

Voici un moyen assez artificiel de le faire sans utiliser explicitement un argument constructeur. Vous devez étendre une classe abstraite paramétrée.

 public class Test {   
    public static void main(String [] args) throws Exception {
        Generic g = new Generic();
        g.initParameter();
    }
}

import java.lang.reflect.ParameterizedType;
public abstract class GenericAbstract<T extends Foo> {
    protected T parameter;

    @SuppressWarnings("unchecked")
    void initParameter() throws Exception, ClassNotFoundException, 
        InstantiationException {
        // Get the class name of this instance's type.
        ParameterizedType pt
            = (ParameterizedType) getClass().getGenericSuperclass();
        // You may need this split or not, use logging to check
        String parameterClassName
            = pt.getActualTypeArguments()[0].toString().split("\\s")[1];
        // Instantiate the Parameter and initialize it.
        parameter = (T) Class.forName(parameterClassName).newInstance();
    }
}

public class Generic extends GenericAbstract<Foo> {
}

public class Foo {
    public Foo() {
        System.out.println("Foo constructor...");
    }
}
 

4voto

hhafez Points 13240

J'ai vraiment besoin d'instancier un T Foo à l'aide d'un paramètre moins constructeur

Réponse est Simple: "tu ne peux pas faire cela" java utilise le type d'effacement de implment génériques qui vous empêcherait de le faire.

Comment peut-on travailler autour de Java limitation?

Une façon (il pourrait y en avoir d'autres) est de passer de l'objet que vous devez passer l'instance de T pour le constructeur de Foo<T>. Ou vous pourriez avoir une méthode setBar(T theInstanceofT); pour obtenir votre T à la place de l'instanciation de la classe elle-même.

1voto

Les médicaments génériques en Java sont généralement plus puissant que dans C#.

Si vous voulez construire un objet, mais sans brancher un constructeur/méthode statique, utilisez un résumé de l'usine. Vous devriez être capable de trouver des informations détaillées et des tutoriels sur le Résumé de l'Usine Modèle de base des modèles de conception de livre, introduction à la programmation orientée objet ou tous sur les interwebs. Il n'en vaut pas la duplication de code ici, sauf pour mentionner que Java est la fermeture de la syntaxe suce.

IIRC, C# est un cas spécial pour la spécification d'un type générique aucun des arguments du constructeur. Cette irrégularité, par définition, suppose que le code client veut utiliser cette forme particulière de la construction et encourage la mutabilité.

À l'aide de la réflexion, pour ce est tout simplement inefficaces. Les médicaments génériques en Java sont un moment de la compilation, la statique-saisie. Il tente de les utiliser lors de l'exécution sont une indication claire de quelque chose qui va pas. La réflexion causes verbose code, la durée d'exécution des échecs, non les dépendances et les vulnérabilités de sécurité. (Class.forName est particulièrement mal.)

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