Item 65: Prefer interfaces to reflection

65. Interface should be selected from reflection

Reflection is a powerful feature, but it also has its drawbacks.

The disadvantages of reflection are as follows.

There are sophisticated applications that take advantage of reflection, such as analytical tools and dependency injection frameworks, but they are moving away from reflection as they see their shortcomings. If you're wondering if you should use reflection, you probably shouldn't.

Case using reflection

Reflection is used in very limited cases. Many programs that have to use a class that cannot be obtained at compile time have an appropriate type and superclass as the type to put the class at compile time (Item64). In this case, you create instances reflectively and access those created instances via an interface or superclass. As an example, in the following program, an instance of the subclass of `Set <String>` specified by the first argument is created, and other command line arguments are packed in the Set and output as standard. If HashSet is taken as the first argument, it will be displayed randomly, but if TreeSet is taken as the first argument, it will be displayed in alphabetical order.

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

}

The technique used above is powerful enough to be used in a full-fledged service provider framework (? Item1). For the most part, this technique is all that is needed for reflection.

The above has two drawbacks.

In the above code, there is a warning when casting. This warning is valid because the cast to `Class <? Extends Set <String >>` is successful even if the name taken as the first argument is not the implementation class of Set. See Item 27 for warning suppression.

A very rare case of using reflection is the dynamic use of multiple versions of a package. Compile the oldest version and dynamically call the method of the new class. (It doesn't come at all)

Recommended Posts

Item 65: Prefer interfaces to reflection
Item 28: Prefer lists to arrays
Item 43: Prefer method references to lambdas
Item 42: Prefer lambdas to anonymous classes
Item 39: Prefer annotations to naming patterns
Item 41: Use marker interfaces to define types
Item 58: Prefer for-each loops to traditional for loops
Item 23: Prefer class hierarchies to tagged classes
Item 64: Refer to objects by their interfaces
Item 61: Prefer primitive types to boxed primitives
Item 81: Prefer concurrency utilities to wait and notify
Item 80: Prefer executors, tasks, and streams to threads
Item 89: For instance control, prefer enum types to readResolve
Item 46: Prefer side-effect-free functions in streams
Item 38: Emulate extensible enums with interfaces
[Rails] Reflection to db using seeds.rb