Java Reflection : Appeler des constructeurs
Instanciez des classes Java par réflexion avec Constructor.newInstance et Class.getDeclaredConstructor.
Créer un objet sans écrire new est l'astuce réflexive qui se cache derrière tout conteneur d'injection de dépendances, désérialiseur et chargeur de plugins : vous disposez d'une Class et vous avez besoin d'une instance. L'objet Constructor<T> représente un constructeur et crée des instances avec newInstance(args...). Ce chapitre explique comment trouver des constructeurs, les appeler avec des arguments, accéder aux constructeurs private, et pourquoi le raccourci Class.newInstance() est déprécié.
Si vous débutez en reflection, commencez par l'introduction à la reflection, puis revenez ici. Les mécanismes décrits ci-dessous correspondent à ce que vous avez vu pour appeler des méthodes et lire des champs par réflexion.
Trouver des constructeurs
Les constructeurs sont recherchés par types de paramètres uniquement — il n'y a pas de nom, car tous les constructeurs partagent le nom de la classe :
Class<User> c = User.class;
Constructor<User> noArg = c.getDeclaredConstructor(); // ()
Constructor<User> twoArg = c.getDeclaredConstructor(String.class, int.class); // (String, int)
Constructor<?>[] pub = c.getConstructors(); // public only
Constructor<?>[] all = c.getDeclaredConstructors(); // any access levelComme partout en reflection, les types de paramètres doivent correspondre exactement (int.class, pas Integer.class), et getConstructor ne voit que les constructeurs public, tandis que getDeclaredConstructor voit aussi les private/protected/package. Notez que Constructor<T> est générique sur la classe qu'il construit, donc newInstance retourne un T typé (contrairement au Object brut de Method.invoke).
Créer des instances avec newInstance
Constructor<User> ctor = User.class.getDeclaredConstructor(String.class, int.class);
User u = ctor.newInstance("ada", 36); // returns a typed UserLes arguments fonctionnent exactement comme avec Method.invoke : un tableau varargs Object[], les primitives étant autoboxées. La différence est qu'il n'y a pas d'objet cible — un constructeur crée la cible. Les exceptions levées par le corps du constructeur sont enveloppées dans InvocationTargetException, exactement comme pour les méthodes ; déroulez-les avec getCause().
Accéder aux constructeurs privés
Les singletons, les classes utilitaires et les builders cachent souvent leur constructeur. La reflection contourne cela facilement avec setAccessible(true) :
Constructor<Singleton> ctor = Singleton.class.getDeclaredConstructor();
ctor.setAccessible(true); // bypass the private modifier
Singleton fresh = ctor.newInstance(); // a SECOND instance — breaks the singleton!C'est à la fois puissant et dangereux : cela annule la garantie du singleton, le contrat « pas d'instances » d'une classe utilitaire, et tout invariant que le constructeur protégeait. (Un singleton enum est la seule forme que la reflection ne peut pas instancier — Constructor.newInstance refuse explicitement les types enum avec IllegalArgumentException, ce qui explique en partie pourquoi le « singleton enum » est le modèle recommandé.)
Pourquoi Class.newInstance() est déprécié
Vous verrez dans l'ancien code le raccourci clazz.newInstance() :
User u = User.class.newInstance(); // DEPRECATED since Java 9Il est déprécié pour deux raisons concrètes :
- Il appelle uniquement le constructeur sans argument. Impossible de passer des arguments.
- Il gère mal les exceptions. Si le constructeur sans argument lève une exception vérifiée,
Class.newInstance()la propage sans la déclarer — ce qui contourne l'analyse des exceptions vérifiées par le compilateur.
Le remplacement est toujours :
User u = User.class.getDeclaredConstructor().newInstance();C'est une ligne de plus, cela appelle un constructeur que vous avez choisi explicitement, et enveloppe les exceptions du constructeur dans InvocationTargetException pour qu'aucune ne s'échappe de manière non déclarée. Utilisez cette forme comme idiome standard, même pour le cas sans argument.
Un exemple complet : une fabrique réflexive minimale
Le programme construit des objets de trois façons : un constructeur public multi-argument, un constructeur private accessible via setAccessible, et l'idiome moderne sans argument — puis il montre un constructeur qui lève une exception enveloppée, et la signature du raccourci déprécié pour comparaison.
Ce que l'exécution nous enseigne :
- La fabrique générique
builda crééWidgetetHiddenà partir d'uneClasset d'un tableau de types de paramètres — sans nommer aucun type dans une expressionnew. Cette signature,<T> T build(Class<T>, Class<?>[], Object...), ressemble essentiellement au cœur d'instanciation d'un conteneur DI : on lui passe un type et des arguments, il retourne une instance. getDeclaredConstructor().newInstance()a produit leWidgetpar défaut, illustrant le remplacement moderne deClass.newInstance(). Préférez toujours cette forme : elle vous permet de choisir le constructeur et achemine les exceptions du constructeur viaInvocationTargetExceptionau lieu de laisser fuir des exceptions vérifiées non déclarées.- L'instance
Hiddencréée par réflexion n'était pas le même objet queHidden.INSTANCE(same instance? false).setAccessible(true)a contourné le constructeurprivateet créé une deuxième instance — preuve concrète que la reflection peut briser la garantie fondamentale d'un singleton. Les singletons défensifs lèvent une exception depuis le constructeur si une instance existe déjà ; les enums sont immunisés par construction. - Le constructeur qui a rejeté une taille négative a levé
IllegalArgumentExceptiondepuis son corps, et cela a été exposé sous forme d'InvocationTargetExceptionavec la vraie cause à l'intérieur — même enveloppement qu'avecMethod.invoke. La validation au moment de la construction est préservée à travers la reflection ; il faut juste dérouler pour la voir. Constructor<T>a retourné unTtypé (Widget,Hidden) sans transtypage, contrairement auObjectbrut deMethod.invoke. Comme le constructeur est générique sur la classe qu'il construit, la fabrique reste type-safe à sa frontière même si tout à l'intérieur est réflexif.