Est-il possible de créer une instance de type générique en Java? Je pense en fonction de ce que j'ai vu que la réponse est no
(en raison de l'effacement du type ), mais je serais intéressé si quelqu'un pouvait voir quelque chose qui me manquait:
class SomeContainer<E>
{
E createContents()
{
return what???
}
}
EDIT: Il s'avère que les jetons Super Type pourraient être utilisés pour résoudre mon problème, mais cela nécessite beaucoup de code basé sur la réflexion, comme l'ont indiqué certaines des réponses ci-dessous.
Je vais laisser cela ouvert pendant un petit moment pour voir si quelqu'un propose quelque chose de radicalement différent de l' article Artima d'Ian Robertson .
Réponses:
Vous avez raison. Tu ne peux pas faire
new E()
. Mais vous pouvez le changer enC'est une douleur. Mais ça marche. L'envelopper dans le modèle d'usine le rend un peu plus tolérable.
la source
Class<?>
référence en utilisant Guava et TypeToken, voir cette réponse pour le code et les liens!Ne sais pas si cela aide, mais lorsque vous sous-classe (y compris de manière anonyme) un type générique, les informations de type sont disponibles via la réflexion. par exemple,
Ainsi, lorsque vous sous-classe Foo, vous obtenez une instance de Bar, par exemple,
Mais c'est beaucoup de travail, et ne fonctionne que pour les sous-classes. Peut être utile cependant.
la source
Foo
n'est pas abstraite. Mais pourquoi ne fonctionne-t-il que sur les sous-classes anonymes de Foo? Supposons que nous fabriquons duFoo
béton (nous omettonsabstract
), pourquoinew Foo<Bar>();
entraînera une erreur, alorsnew Foo<Bar>(){};
que non? (Exception: "La classe ne peut pas être convertie en ParameterizedType")<E>
enclass Foo<E>
est pas lié à un type particulier. Vous verrez le comportement exceptionnel à chaque foisE
est pas statiquement lié, comme dans:new Foo<Bar>()
,new Foo<T>() {...}
ouclass Fizz <E> extends Foo<E>
. Le premier cas n'est pas lié statiquement, il est effacé au moment de la compilation. Le second cas remplace une autre variable de type (T) à la place deE
mais n'est toujours pas lié. Et dans le dernier cas, il devrait être évident qu'ilE
n'est toujours pas lié.class Fizz extends Foo<Bar>
- dans ce cas, les utilisateurs deFizz
get quelque chose qui est unFoo<Bar>
et ne peut être rien d'autre qu'un aFoo<Bar>
. Donc, dans ce cas, le compilateur est heureux d'encoder ces informations dans les métadonnées de classe pourFizz
et de les rendre disponibles en tant queParameterizedType
code de réflexion. Lorsque vous créez une classe interne anonyme commenew Foo<Bar>() {...}
s'il faisait la même chose, sauf queFizz
le compilateur génère un nom de classe "anonyme" que vous ne connaîtrez pas tant que la classe externe ne sera pas compilée.Foo<Bar<Baz>>
,. Vous allez créer une instanceParameterizedTypeImpl
qui ne peut pas être explicitement créée. Par conséquent, c'est une bonne idée de vérifier sigetActualTypeArguments()[0]
renvoie unParameterizedType
. Si tel est le cas, vous souhaitez obtenir le type brut et créer une instance de celui-ci à la place.Dans Java 8, vous pouvez utiliser l'
Supplier
interface fonctionnelle pour y parvenir assez facilement:Vous construirez cette classe comme ceci:
La syntaxe
String::new
sur cette ligne est une référence constructeur .Si votre constructeur prend des arguments, vous pouvez utiliser une expression lambda à la place:
la source
SomeContainer stringContainer = new SomeContainer(String::new);
?Vous aurez besoin d'une sorte d'usine abstraite d'une sorte ou d'une autre pour passer la balle à:
la source
Factory<>
est une interface et il n'y a donc pas de corps. Le fait est que vous avez besoin d'une couche d'indirections pour passer la balle aux méthodes qui "connaissent" le code requis pour construire une instance. Il est préférable de le faire avec du code normal plutôt que métalinguistiqueClass
ouConstructor
car la réflexion apporte tout un monde de souffrance.SomeContainer<SomeElement> cont = new SomeContainer<>(SomeElement::new);
la source
class GenericHome<T> extends Home<T>{}
Si vous avez besoin d'une nouvelle instance d'un argument de type dans une classe générique, demandez à vos constructeurs d'exiger sa classe ...
Usage:
Avantages:
Les inconvénients:
Foo<L>
preuve. Pour commencer...newInstance()
lancera un wobbler si la classe d'argument type n'a pas de constructeur par défaut. Cependant, cela s'applique à toutes les solutions connues.la source
Vous pouvez le faire maintenant et cela ne nécessite pas un tas de code de réflexion.
Bien sûr, si vous devez appeler le constructeur qui nécessitera une réflexion, mais qui est très bien documenté, cette astuce ne l'est pas!
Voici le JavaDoc pour TypeToken .
la source
Pensez à une approche plus fonctionnelle: au lieu de créer du E à partir de rien (ce qui est clairement une odeur de code), passez une fonction qui sait en créer un, c'est à dire
la source
Exception
utilisation de la captureSupplier<E>
.De Java Tutorial - Restrictions sur les génériques :
Impossible de créer des instances de paramètres de type
Vous ne pouvez pas créer une instance d'un paramètre de type. Par exemple, le code suivant provoque une erreur de compilation:
Pour contourner ce problème, vous pouvez créer un objet d'un paramètre de type par réflexion:
Vous pouvez appeler la méthode append comme suit:
la source
Voici une option que j'ai trouvée, cela peut aider:
EDIT: Alternativement, vous pouvez utiliser ce constructeur (mais il nécessite une instance de E):
la source
Si vous ne voulez pas taper deux fois le nom de la classe pendant l'instanciation comme dans:
Vous pouvez utiliser la méthode d'usine:
Comme dans:
la source
Malheureusement, Java ne permet pas ce que vous voulez faire. Voir la solution de contournement officielle :
la source
Lorsque vous travaillez avec E au moment de la compilation, vous ne vous souciez pas vraiment du type générique réel "E" (soit vous utilisez la réflexion, soit vous travaillez avec une classe de base de type générique), alors laissez la sous-classe fournir une instance de E.
la source
Vous pouvez utiliser:
Mais vous devez fournir le nom de classe exact, y compris les packages, par exemple.
java.io.FileInputStream
. Je l'ai utilisé pour créer un analyseur d'expressions mathématiques.la source
foo.getClass().getName()
. D'où vient cette instance? J'en passe actuellement un à un constructeur dans le projet sur lequel je travaille actuellement.J'espère que ce n'est pas trop tard pour aider !!!
Java est de type sécurisé, seul Object est capable de créer une instance.
Dans mon cas, je ne peux pas passer de paramètres à la
createContents
méthode. Ma solution utilise étend au lieu de toutes les réponses ci-dessous.C'est mon cas d'exemple dont je ne peux pas passer les paramètres.
L'utilisation de la réflexion crée une erreur d'exécution, si vous étendez votre classe générique avec aucun type d'objet. Pour étendre votre type générique en objet, convertissez cette erreur en erreur de compilation.
la source
Utilisez la
TypeToken<T>
classe:la source
(T) new TypeToken<T>(getClass()){}.getRawType().newInstance();
Je pensais pouvoir le faire, mais assez déçu: ça ne marche pas, mais je pense que ça vaut quand même la peine d'être partagé.
Peut-être que quelqu'un peut corriger:
Cela produit:
La ligne 26 est celle avec le
[*]
.La seule solution viable est celle de @JustinRudd
la source
Une amélioration de la réponse de @ Noah.
Raison du changement
a] Est plus sûr si plus d'un type générique est utilisé au cas où vous auriez changé la commande.
b] Une signature de type générique de classe change de temps en temps afin que vous ne soyez pas surpris par des exceptions inexpliquées dans le runtime.
Code robuste
Ou utilisez la doublure
Code d'une ligne
la source
ce que vous pouvez faire c'est -
Déclarez d'abord la variable de cette classe générique
Ensuite, faites-en un constructeur et instanciez cet objet
Ensuite, utilisez-le où vous voulez l'utiliser
exemple-
1
2
3.
la source
Comme vous l'avez dit, vous ne pouvez pas vraiment le faire à cause de l'effacement des caractères. Vous pouvez le faire en utilisant la réflexion, mais cela nécessite beaucoup de code et beaucoup de gestion des erreurs.
la source
Si vous voulez dire,
new E()
c'est impossible. Et j'ajouterais que ce n'est pas toujours correct - comment savoir si E a un constructeur public sans argument? Mais vous pouvez toujours déléguer la création à une autre classe qui sait comment créer une instance - cela peut êtreClass<E>
ou votre code personnalisé comme celui-cila source
la source
SomeContainer
est tout simplementObject
. Par conséquent,this.getClass().getGenericSuperclass()
renvoie unClass
(classe java.lang.Object), pas unParameterizedType
. Cela a déjà été signalé par stackoverflow.com/questions/75175/… .Vous pouvez y parvenir avec l'extrait de code suivant:
la source
Il existe différentes bibliothèques qui peuvent résoudre
E
pour vous en utilisant des techniques similaires à ce que l'article Robertson a discuté. Voici une implémentationcreateContents
qui utilise TypeTools pour résoudre la classe brute représentée par E:Cela suppose que getClass () se résout en une sous-classe de SomeContainer et échouera sinon car la valeur paramétrée réelle de E aura été effacée au moment de l'exécution si elle n'est pas capturée dans une sous-classe.
la source
Voici une implémentation
createContents
qui utilise TypeTools pour résoudre la classe brute représentée parE
:Cette approche ne fonctionne que si
SomeContainer
est sous-classée, de sorte que la valeur réelle deE
est capturée dans une définition de type:Sinon, la valeur de E est effacée lors de l'exécution et n'est pas récupérable.
la source
Vous pouvez avec un chargeur de classe et le nom de la classe, éventuellement certains paramètres.
la source
Voici une solution améliorée, basée sur
ParameterizedType.getActualTypeArguments
, déjà mentionnée par @noah, @Lars Bohl et quelques autres.Première petite amélioration de l'implémentation. Factory ne doit pas renvoyer d'instance, mais un type. Dès que vous retournez une instance en utilisant,
Class.newInstance()
vous réduisez la portée de l'utilisation. Parce que seuls les constructeurs sans arguments peuvent être invoqués comme ceci. Une meilleure façon est de renvoyer un type et de permettre au client de choisir le constructeur qu'il souhaite invoquer:Voici quelques exemples d'utilisation. @Lars Bohl a montré seulement un moyen de signe pour obtenir générique réifié via l'extension. @noah uniquement via la création d'une instance avec
{}
. Voici des tests pour démontrer les deux cas:Remarque: vous pouvez forcer les clients de
TypeReference
toujours utiliser{}
lorsque l' instance est créée en faisant cette classe abstraite:public abstract class TypeReference<T>
. Je ne l'ai pas fait, seulement pour montrer le cas de test effacé.la source