[JAVA] Rubrique 65: Préférez les interfaces à la réflexion

65. L'interface doit être choisie à partir de la réflexion

La réflexion est une fonctionnalité puissante, mais elle a aussi ses inconvénients.

Les inconvénients de la réflexion sont les suivants.

Il existe des applications sophistiquées qui tirent parti de la réflexion, comme les outils d'analyse et les cadres d'injection de dépendances, mais elles s'éloignent de la réflexion à mesure que les inconvénients de la réflexion deviennent évidents. Si vous vous demandez si vous devriez utiliser la réflexion, vous ne devriez probablement pas.

Cas utilisant la réflexion

La réflexion est utilisée dans des cas très limités. De nombreux programmes qui doivent utiliser une classe qui ne peut pas être obtenue au moment de la compilation ont un type et une superclasse appropriés comme type pour placer la classe au moment de la compilation (Item64). Dans ce cas, nous créons des instances de manière réfléchie et accédons à ces instances créées via des interfaces et des superclasses. Par exemple, dans le programme suivant, une instance de la sous-classe de `` Set '' spécifiée par le premier argument est créée, et d'autres arguments de ligne de commande sont emballés dans l'ensemble et sortis en standard. Si HashSet est pris comme premier argument, il sera affiché au hasard, mais si TreeSet est pris comme premier argument, il sera affiché dans l'ordre alphabétique.

package tryAny.effectiveJava;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Set;

public class Reflection {
    // Reflective instantiation with interface access
    public static void main(String[] args) {
        // Translate the class name into a Class object
        Class<? extends Set<String>> cl = null;
        try {
            cl = (Class<? extends Set<String>>) // Unchecked cast!
            Class.forName(args[0]);
        } catch (ClassNotFoundException e) {

            fatalError("Class not found.");
        }
        // Get the constructor
        Constructor<? extends Set<String>> cons = null;
        try {
            cons = cl.getDeclaredConstructor();
        } catch (NoSuchMethodException e) {
            fatalError("No parameterless constructor");
        }
        // Instantiate the set
        Set<String> s = null;
        try {
            s = cons.newInstance();
        } catch (IllegalAccessException e) {
            fatalError("Constructor not accessible");
        } catch (InstantiationException e) {
            fatalError("Class not instantiable.");
        } catch (InvocationTargetException e) {
            fatalError("Constructor threw " + e.getCause());
        } catch (ClassCastException e) {
            fatalError("Class doesn't implement Set");
        }
        // Exercise the set
        s.addAll(Arrays.asList(args).subList(1, args.length));
        System.out.println(s);
    }

    private static void fatalError(String msg) {
        System.err.println(msg);
        System.exit(1);
    }

}

La technique utilisée ci-dessus est suffisamment puissante pour être utilisée dans un cadre de fournisseur de services à part entière (? Item1). Cette technique est presque tout ce dont vous avez besoin pour la réflexion.

Ce qui précède présente deux inconvénients.

Dans le code ci-dessus, il y a un avertissement lors de la diffusion. Cet avertissement est valide car le transtypage en Class <? Extends Set <String >> `` réussira même si le nom pris comme premier argument n'est pas la classe d'implémentation de Set. Voir le point 27 pour la suppression des avertissements.

Un cas très rare d'utilisation de la réflexion est l'utilisation dynamique de plusieurs versions d'un package. Compilez la version la plus ancienne et appelez dynamiquement la méthode de la nouvelle classe. (Ça ne vient pas du tout)

Recommended Posts

Rubrique 65: Préférez les interfaces à la réflexion
Élément 28: Préférer les listes aux tableaux
Rubrique 43: Préférez les références de méthode aux lambdas
Point 42: Préférez les lambdas aux classes anonymes
Élément 39: Préférez les annotations aux modèles de dénomination
Point 41: Utiliser les interfaces de marqueurs pour définir les types
Point 58: Préférez les boucles for-each aux boucles for traditionnelles
Élément 23: Préférez les hiérarchies de classes aux classes balisées
Rubrique 64: Se référer aux objets par leurs interfaces
Point 61: Préférez les types primitifs aux primitives encadrées
Élément 81: Préférez les utilitaires de concurrence pour attendre et notifier
Élément 80: Préférez les exécuteurs, les tâches et les flux aux threads
Élément 89: Pour le contrôle d'instance, préférez les types enum à readResolve
Point 46: Préférez les fonctions sans effets secondaires dans les flux
Point 38: émuler des énumérations extensibles avec des interfaces