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
, et j’ai vraiment besoin d’instancier un T
dans Foo
utilisant un constructeur sans paramètre. Comment peut-on contourner la limitation de Java?
Une option consiste à transmettre Bar.class
(ou tout autre type qui vous intéresse – toute manière de spécifier la référence de Class
appropriée) et à conserver cette valeur en tant que champ:
public class Test { public static void main(Ssortingng [] args) throws Exception // Just for simplicity! { Generic x = new Generic (Bar.class); Bar y = x.buildOne(); } } public class Generic { private Class clazz; public Generic(Class 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 reflection.
Et c’est l’implémentation de Factory, comme Jon Skeet l’a suggéré :
interface Factory { T factory(); } class Araba { //static inner class for Factory implementation public static class ArabaFactory implements Factory { public Araba factory() { return new Araba(); } } public Ssortingng toSsortingng() { return "Abubeee"; } } class Generic { private T var; Generic(Factory fact) { System.out.println("Constructor with Factory 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(Ssortingng[] ssortingng) { Generic gen = new Generic (new Araba.ArabaFactory()); System.out.print(gen.get()); } }
Sortie:
Constructor with Factory parameter Abubeee
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(Ssortingng [] args) throws Exception { Generic g = new Generic(); g.initParameter(); } } import java.lang.reflect.ParameterizedType; public abstract class GenericAbstract { 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 Ssortingng parameterClassName = pt.getActualTypeArguments()[0].toSsortingng().split("\\s")[1]; // Instantiate the Parameter and initialize it. parameter = (T) Class.forName(parameterClassName).newInstance(); } } public class Generic extends GenericAbstract { } public class Foo { public Foo() { System.out.println("Foo constructor..."); } }
J’ai vraiment besoin d’instancier un T dans Foo en utilisant un constructeur sans paramètre
La réponse simple est “vous ne pouvez pas faire ça” java utilise l’effacement de type pour implémenter des génériques, ce qui vous empêcherait de le faire.
Comment peut-on contourner la limitation de Java?
Une façon (il pourrait y en avoir d’autres) consiste à transmettre l’object que vous transmettez l’instance de T au constructeur de Foo
. Ou vous pourriez avoir une méthode setBar(T theInstanceofT);
pour obtenir votre T au lieu d’instancier dans la classe elle-même.
Les génériques en Java sont généralement plus puissants qu’en C #.
Si vous voulez construire un object mais sans câbler une méthode constructeur / statique, utilisez une fabrique abstraite. Vous devriez être en mesure de trouver des informations détaillées et des didacticiels sur le motif d’usine abstraite dans n’importe quel cahier de motifs de conception de base, introduction à la POO ou sur l’ensemble des interwebs. Cela ne vaut pas la peine de dupliquer le code ici, sauf pour mentionner que la syntaxe de fermeture de Java est mauvaise.
IIRC, C # a un cas particulier pour spécifier un type générique avec un constructeur no-args. Cette irrégularité, par définition, présuppose que le code client veut utiliser cette forme particulière de construction et encourage la mutabilité.
Utiliser la reflection pour cela est juste faux. Les génériques en Java sont une fonctionnalité de compilation statique au moment de la compilation. Les tentatives d’utilisation à l’exécution sont une indication claire de quelque chose qui ne va pas. Reflection provoque du code explicite, des échecs d’exécution, des dépendances non vérifiées et des vulnérabilités de sécurité. ( Class.forName
est particulièrement mauvais.)
De https://stackoverflow.com/a/2434094/848072 . Vous avez besoin d’un constructeur par défaut pour la classe T.
import java.lang.reflect.ParameterizedType; class Foo { public bar() { ParameterizedType superClass = (ParameterizedType) getClass().getGenericSuperclass(); Class type = (Class) superClass.getActualTypeArguments()[0]; try { T t = type.newInstance(); //Do whatever with t } catch (Exception e) { // Oops, no default constructor throw new RuntimeException(e); } } }
import java.lang.reflect.ParameterizedType; class Foo { public bar() { ParameterizedType superClass = (ParameterizedType) getClass().getGenericSuperclass(); Class type = (Class) superClass.getActualTypeArguments()[0]; try { T t = type.newInstance(); //Do whatever with t } catch (Exception e) { // Oops, no default constructor throw new RuntimeException(e); } } }
Je pourrais le faire dans une configuration de test JUnit.
Je voulais tester une façade Hibernate, donc je cherchais un moyen générique de le faire. Notez que la façade implémente également une interface générique. Ici, T est la classe de firebase database et U la clé primaire. Ifacade
est une façade pour accéder à l’object de firebase database T avec la clé primaire U.
public abstract class GenericJPAController> { protected static EntityManagerFactory emf; /* The properties definition is straightforward*/ protected T testObject; protected C facadeManager; @BeforeClass public static void setUpClass() { try { emf = Persistence.createEntityManagerFactory("my entity manager factory"); } catch (Throwable ex) { System.err.println("Failed to create sessionFactory object." + ex); throw new ExceptionInInitializerError(ex); } } @AfterClass public static void tearDownClass() { } @Before public void setUp() { /* Get the class name*/ Ssortingng className = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[2].getTypeName(); /* Create the instance */ try { facadeManager = (C) Class.forName(className).newInstance(); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) { Logger.getLogger(GenericJPAController.class.getName()).log(Level.SEVERE, null, ex); } createTestObject(); } @After public void tearDown() { } /** * Test of testFindTEntities_0args method, of class * GenericJPAController>. * @throws java.lang.ClassNotFoundException * @throws java.lang.NoSuchMethodException * @throws java.lang.InstantiationException * @throws java.lang.IllegalAccessException */ @Test public void testFindTEntities_0args() throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException { /* Example of instance usage. Even intellisense (NetBeans) works here!*/ try { List lista = (List ) facadeManager.findAllEntities(); lista.stream().forEach((ct) -> { System.out.println("Find all: " + ssortingngReport()); }); } catch (Throwable ex) { System.err.println("Failed to access object." + ex); throw new ExceptionInInitializerError(ex); } } /** * * @return */ public abstract Ssortingng ssortingngReport(); protected abstract T createTestObject(); protected abstract T editTestObject(); protected abstract U getTextObjectIndex(); }
Solution rapide qui a fonctionné pour moi. Je vois qu’il y a déjà une réponse à cela et que ce n’est peut-être même pas la meilleure façon de s’y prendre. Aussi, pour ma solution, vous aurez besoin de Gson .
Cependant, j’ai rencontré une situation où je devais créer une instance d’une classe générique de type java.lang.reflect.Type
.
Le code suivant créera une instance de la classe souhaitée avec des variables d’instance NULL.
T object = new Gson().fromJson("{}", myKnownType);
Où myKnownType
est connu d’avance et obtenu via TypeToken.getType()
.
Vous pouvez maintenant définir les propriétés appropriées sur cet object. Encore une fois, ce n’est peut-être pas la meilleure façon de procéder, mais cela fonctionne comme une solution rapide si c’est ce dont vous avez besoin.
Pour Java 8 ….
Il y a une bonne solution sur https://stackoverflow.com/a/36315051/2648077 .
Ceci utilise l’interface fonctionnelle Java 8 Supplier