[JAVA] Punkt 65: Schnittstellen der Reflexion vorziehen

65. Die Schnittstelle sollte aus der Reflexion ausgewählt werden

Reflexion ist ein mächtiges Merkmal, hat aber auch seine Nachteile.

Die Nachteile der Reflexion sind wie folgt.

Es gibt hochentwickelte Anwendungen, die die Reflexion nutzen, wie z. B. Analysewerkzeuge und Frameworks für die Abhängigkeitsinjektion, aber sie entfernen sich von der Reflexion, wenn die Nachteile der Reflexion offensichtlich werden. Wenn Sie sich fragen, ob Sie Reflexion verwenden sollten, sollten Sie dies wahrscheinlich nicht tun.

Fall mit Reflexion

Reflexion wird in sehr begrenzten Fällen verwendet. Viele Programme, die eine Klasse verwenden müssen, die zur Kompilierungszeit nicht abgerufen werden kann, haben einen geeigneten Typ und eine geeignete Oberklasse als Typ, um die Klasse zur Kompilierungszeit zu setzen (Item64). In diesem Fall erstellen wir Instanzen reflektierend und greifen über Schnittstellen und Oberklassen auf diese erstellten Instanzen zu. Im folgenden Programm wird beispielsweise eine Instanz der durch das erste Argument angegebenen Unterklasse von `` Set <String> `erstellt, und andere Befehlszeilenargumente werden in Set gepackt und standardmäßig ausgegeben. Wenn HashSet als erstes Argument verwendet wird, wird es zufällig angezeigt. Wenn TreeSet als erstes Argument verwendet wird, wird es in alphabetischer Reihenfolge angezeigt.

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);
    }

}

Die oben verwendete Technik ist leistungsfähig genug, um in einem vollwertigen Service Provider-Framework (? Item1) verwendet zu werden. Diese Technik ist fast alles, was Sie zum Nachdenken benötigen.

Das Obige hat zwei Nachteile.

Im obigen Code gibt es beim Casting eine Warnung. Diese Warnung ist gültig, da die Umwandlung in `` Class <? Extends Set <String >> `erfolgreich ist, auch wenn der als erstes Argument verwendete Name nicht die Implementierungsklasse von Set ist. Siehe Punkt 27 zur Unterdrückung von Warnungen.

Ein sehr seltener Fall der Verwendung von Reflection ist die dynamische Verwendung mehrerer Versionen eines Pakets. Kompilieren Sie die älteste Version und rufen Sie die Methode der neuen Klasse dynamisch auf. (Es kommt überhaupt nicht)

Recommended Posts

Punkt 65: Schnittstellen der Reflexion vorziehen
Punkt 28: Listen Arrays vorziehen
Punkt 43: Bevorzugen Sie Methodenverweise auf Lambdas
Punkt 42: Bevorzugen Sie Lambdas gegenüber anonymen Klassen
Punkt 39: Bevorzugen Sie Anmerkungen gegenüber Namensmustern
Punkt 41: Verwenden Sie Markierungsschnittstellen, um Typen zu definieren
Punkt 58: Bevorzugen Sie für jede Schleife herkömmliche Schleifen
Punkt 23: Bevorzugen Sie Klassenhierarchien gegenüber markierten Klassen
Punkt 64: Verweisen Sie auf Objekte anhand ihrer Schnittstellen
Punkt 61: Bevorzugen Sie primitive Typen gegenüber primitiven Boxen
Punkt 81: Bevorzugen Sie Parallelitätsdienstprogramme, um zu warten und zu benachrichtigen
Punkt 80: Ziehen Sie Executoren, Aufgaben und Streams Threads vor
Punkt 89: Für die Steuerung bevorzugen Sie Aufzählungstypen gegenüber readResolve
Punkt 46: Bevorzugen Sie nebenwirkungsfreie Funktionen in Streams
Punkt 38: Emulieren erweiterbarer Enums mit Schnittstellen