Java - Relection

Created:2018-03-15  Last modified:2019-10-27


  1. Introduction

    Reflection: load new class at runtime. Reflection is the foundation for building Dependency Injection.

    Check class attributes, e.g. check the methods inside a class, annotations.

    When a program is running, Java runtime maintains information that maps objects to its class file. When objects need to call methods, it loads them from the class files.

    Field, Method, Constructor -- 3 important classes in the java.lang.reflect package.

  2. Create objects

    Get class objects

    1. Java defined a class called "Class", every class has a static property which is the object of the class's Class
                      // Example, String has a corresponding class, which is Class<String>
                      Class<String> cObject = String.class; // return the object of Class<String>
                      Class<? extends ;String> cObject2 = "Qinnan".getClass(); // return the same object.
                      cObject == cObject2; // true
                  

      The runtime maintains a unique Class object for each type.So String.class == "Name".getClass()

    2. A class object can also be initiated from a string path to that class definition. But you have to do a manual cast.
                      Class<?> target = Class.forName("reflection.test.Target");
                      Target t = (Target) target.newInstance();
                  
    3. The objects of Class have a list of methods that can initiate the corresponding class objects. For example,
                      Class<String> strC = String.class;
                      String str = strC.newInstance();
                  
                      public final class Class<T> ...{
                          public T newInstance() throws InstantiationException, IllegalAccessException
                          {
                              Constructor<T> c = ..;
                              return c.newInstance(...);
                          }
                      }
                  

    Constructor class

    1. (Class).newInstance: Class<T> returns a T object. Must be sure the class has a default constructor
    2. (Class).getConstructor(Class<?>... param): Constructor<T>: e.g. responseClass.getConstructor(LocalInstance.class, String.class, Map.class)
    3. (Constructor).newInstance(Object ...): T, create an object
          byte [] a = {113,110};
          Constructor<String> c = str3.getConstructor(a.getClass());
      
          String x = c.newInstance(a);
          System.out.println(x);
      
              Class<TargetClass> tc1 = TargetClass.class;
              try{
                  Constructor<TargetClass> con = tc1.getConstructor(String.class, String.class, String.class);
                  try{
                      TargetClass t1 = con.newInstance("private", "protected", "public");
                      System.out.println(t1.getPublicValue());
                  }catch (InstantiationException e){
                      System.out.println("cannot instantiate");
                  }catch (InvocationTargetException e){
                      System.out.println("cannot invoke this method");
                  }catch (IllegalAccessException e){
                      System.out.println("cannot access this method");
                  }
              }catch (NoSuchMethodException e){
                  System.out.println("no such constructor");
              }
      
  3. Methods

    A Class object .getMethods() will return a Method[] array, which includes all the public static/non-static methods, including the inherited methods from direct and indirect parent classes.

    A Class object .getDeclaredMethod(String, Class<?>...): return a Method

    Call method

    A Method object has a method "invoke(Object obj, Object ... ):Object".
    The first object is the object that actually calls this method. It is null if static.
    The following object are the parameters.
    The returned object is the return value, need to be cast.

    Method's annotation, exceptions, parameter, return type, modifiers, parameter annotations,

    1. How to distinguish if the method is defined in the current class or super class?
      Method [] ms = c.getMethods();
      for(Method i : ms){
      if(i.getDeclaringClass() == c){ // i.getDeclaringClass returns the closest class that override this method.
      // for example, if the toString is overrided in this class, then (toString).getDeclaringClass() will return this class.
          System.out.println(i.getName());
      }
      
      
    2. Reflection gives default parameters with Parameter annotation
                      if(i.getName()=="setName"){
                          Annotation[][] pas = i.getParameterAnnotations();
                          try {
                              StringParameter sp = (StringParameter) pas[0][0];
                              if(!sp.requiredValue()){
                                  try{
                                      i.invoke(ws, sp.value());
                                  }catch (InvocationTargetException e){
                                      System.out.println("cannot invoke this method");
                                  }catch (IllegalAccessException e){
                                      System.out.println("cannot access this method");
                                  }
                              }
                          }catch (Exception e){
                              System.out.println("Default parameters not supported");
                          }
                      }
      
  4. References