656 votes

Créer une instance de type générique en Java?

Est-il possible de créer une instance d'un type générique en Java? Je pense d'après ce que j'ai vu que la réponse est - no (en raison du type d'effacement), mais je serais intéressé si quelqu'un peut voir quelque chose qui m'échappe:

class SomeContainer<E>
{
    E createContents()
    {
        return what???
    }
}

EDIT: Il s'avère que Super Jetons de Type pourrait être utilisé pour résoudre mon problème, mais il faut beaucoup de réflexion à base de code, comme quelques réponses ci-dessous indiquée.

Je vais quitter cette ouvert pour un peu de temps pour voir si quelqu'un arrive avec quelque chose de radicalement différent de Ian Robertson Artima Article.

377voto

Justin Rudd Points 2514

Vous êtes correct. Vous ne pouvez pas faire "nouvel E". Mais vous pouvez la changer

private static class SomeContainer<E>
{
    E createContents(Class<E> clazz)
    {
        return clazz.newInstance();
    }
}

C'est une douleur. Mais il fonctionne. En l'enveloppant dans le modèle de fabrique rend un peu plus tolérable.

145voto

noah Points 9333

Je ne sais pas si cela aide, mais quand vous sous-classe (y compris de façon anonyme) d'un type générique, le type d'informations est disponible via la réflexion. par exemple,

public abstract class Foo<E> {

  public E instance;  

  public Foo() throws Exception {
    instance = ((Class)((ParameterizedType)this.getClass().
       getGenericSuperclass()).getActualTypeArguments()[0]).newInstance();
    ...
  }

}

Donc, quand vous sous-classe Foo, vous obtenez une instance de Bar par exemple,

// notice that this in anonymous subclass of Foo
assert( new Foo<Bar>() {}.instance instanceof Bar );

Mais c'est beaucoup de travail, et ne fonctionne que pour les sous-classes. Peut être pratique si.

90voto

Vous aurez besoin d'une sorte de résumé de l'usine d'une sorte ou d'une autre de passer la balle à:

interface Factory<E> {
    E create();
}

class SomeContainer<E> {
    private final Factory<E> factory;
    SomeContainer(Factory<E> factory) {
        this.factory = factory;
    }
    E createContents() {
        return factory.create();
    }
}

27voto

Lars Bohl Points 349
package org.foo.com;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * Basically the same answer as noah's.
 */
public class Home<E>
{

    @SuppressWarnings ("unchecked")
    public Class<E> getTypeParameterClass()
    {
        Type type = getClass().getGenericSuperclass();
        ParameterizedType paramType = (ParameterizedType) type;
        return (Class<E>) paramType.getActualTypeArguments()[0];
    }

    private static class StringHome extends Home<String>
    {
    }

    private static class StringBuilderHome extends Home<StringBuilder>
    {
    }

    private static class StringBufferHome extends Home<StringBuffer>
    {
    }   

    /**
     * This prints "String", "StringBuilder" and "StringBuffer"
     */
    public static void main(String[] args) throws InstantiationException, IllegalAccessException
    {
        Object object0 = new StringHome().getTypeParameterClass().newInstance();
        Object object1 = new StringBuilderHome().getTypeParameterClass().newInstance();
        Object object2 = new StringBufferHome().getTypeParameterClass().newInstance();
        System.out.println(object0.getClass().getSimpleName());
        System.out.println(object1.getClass().getSimpleName());
        System.out.println(object2.getClass().getSimpleName());
    }

}

25voto

R2D2M2 Points 91

Si vous avez besoin d'une nouvelle instance d'un type d'argument à l'intérieur d'une classe générique, puis faire votre constructeur de la demande de sa classe...

public final class Foo<T> {

    private Class<T> typeArgumentClass;

    public Foo(Class<T> typeArgumentClass) {

        this.typeArgumentClass = typeArgumentClass;
    }

    public void doSomethingThatRequiresNewT() throws Exception {

        T myNewT = typeArgumentClass.newInstance();
        ...
    }
}

Utilisation:

Foo<Bar> barFoo = new Foo<Bar>(Bar.class);
Foo<Etc> etcFoo = new Foo<Etc>(Etc.class);

Pour:

  • Beaucoup plus simple (et moins problématique) que Robertson Super Type de Jeton (STT).
  • Beaucoup plus efficace que la STT approche (qui va manger votre téléphone portable pour le petit déjeuner).

Inconvénients:

  • On ne peut pas passer de la Classe à un constructeur par défaut (c'est pourquoi Foo est définitive). Si vous avez vraiment besoin d'un constructeur par défaut, vous pouvez toujours ajouter une méthode de définition, mais alors vous devez vous rappeler de lui donner un appel plus tard.
  • Robertson objection... Barres de Plus qu'un mouton noir (bien préciser le type de l'argument de la classe une fois de plus ne sera pas exactement vous tuer). Et contrairement à Robertson revendications, cela ne viole pas le SÈCHE principal de toute façon parce que le compilateur va assurer l'exactitude des types.
  • Pas entièrement Foo<L>la preuve. Pour commencer... newInstance() va jeter un wobbler si l'argument de type de classe n'a pas de constructeur par défaut. Cela s'applique à toutes les solutions connues mais tout de même.
  • Manque total d'encapsulation de la STT approche. Pas une grosse affaire si, compte tenu de la scandaleuse des performances de STT).

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