反射相关API整理

  • 概述

    很多主流的IOC框架,像移动端的Retrofit、服务端的Spring等的核心思想都是通过反射去实现的,阅读源码的时候有很多反射相关的API调用,而这些东西在平时开发中很少用到,时间久了就会忘记,写下这篇文章来整理复习一下,顺便做个记录。

  • 继承关系

    image

  • Class类

    • 获取Class对象

      class对象包含了这了类的fields、methods、constructors。

      Example:World worldObj,获取它的class对象的方式:

      Class.forName(World的全限定类名)、worldObj.getClass()、World.class

    • Field

      Field对象可以通过Class对象的getField(String fieldName)、getFields()、getDeclaredField(String fieldName)、getDeclaredFields()方法获得。

      getField(String fieldName)和getFields()都只能获取public修饰的字段,注意kotlin不管是var还是val都是private权限的,加上lateinit之后会变成public的,可以这样理解,懒加载意味着在未来某个时刻初始化,所以允许反射来获取字段进行修改。

      getFields()可以获取包括父类以上的superClass的同名Field,比如说A extends B,A和B中都有名为“name”的Field,则getFields返回的数组中会有两个名字为“name”的Field,但是调用他们的getDeclaredClass()就会知道它们不属于同一个类。

      getDeclaredField(String fieldName)和getDeclaredFields()方法都是可以获取任何权限修饰符修饰的字段。

      /**
       * Returns a {@code Field} object that reflects the specified declared
       * field of the class or interface represented by this {@code Class}
       * object. The {@code name} parameter is a {@code String} that specifies
       * the simple name of the desired field.
      **/
      @FastNative
      public native Field getDeclaredField(String name) throws NoSuchFieldException;
      

      而且可以返回包括当前类实现的接口中定义的且被重写的Field(必须是重写了的)。

    • Constructor

      getConstructor()和getConstructors()都会返回public修饰的构造方法,getDeclaredConstructor()和getDeclaredConstructors()都会返回所有构造方法,可以是non-public的,他们都只返回当前类中的构造方法。

    • Method

      Method对象通过Class对象获取,通过getMethod()或getDeclaredMethod()获得,第一个参数是方法名(String),后面是可变参数数组,表示方法参数类型。

      • getMethod:
        @CallerSensitive
        public Method getMethod(String name, Class<?>... parameterTypes)
           throws NoSuchMethodException, SecurityException {
           return getMethod(name, parameterTypes, true);
        }
        
      • getDeclaredMethod:
        @CallerSensitive
        public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
            throws NoSuchMethodException, SecurityException {
            return getMethod(name, parameterTypes, false);
        }
        

        可以看到,最终都调用了三个参数的getMethod方法,这个方法是private类型的:

        private Method getMethod(String name, Class<?>[] parameterTypes, boolean recursivePublicMethods)
              throws NoSuchMethodException {
          if (name == null) {
              throw new NullPointerException("name == null");
          }
          if (parameterTypes == null) {
              parameterTypes = EmptyArray.CLASS;
          }
          for (Class<?> c : parameterTypes) {
              if (c == null) {
                  throw new NoSuchMethodException("parameter type is null");
              }
          }
          //根据recursivePublicMethods(recursive是递归的意思)的值来判断是查找包括父类在内的所有方法还是只查找当前类中的方法
          Method result = recursivePublicMethods ? getPublicMethodRecursive(name, parameterTypes)
                                                 : getDeclaredMethodInternal(name, parameterTypes);
          // Fail if we didn't find the method or it was expected to be public.
          //recursivePublicMethods && !Modifier.isPublic(result.getAccessFlags()))表示调用getMethod方法的时候则方法必须是public修饰的
          if (result == null ||
              (recursivePublicMethods && !Modifier.isPublic(result.getAccessFlags()))) {
              throw new NoSuchMethodException(getName() + "." + name + " "
                      + Arrays.toString(parameterTypes));
          }
          return result;
        }
        

        其内部调用的方法如下:

        private Method getPublicMethodRecursive(String name, Class<?>[] parameterTypes) {
          // search superclasses
          for (Class<?> c = this; c != null; c = c.getSuperclass()) {
              Method result = c.getDeclaredMethodInternal(name, parameterTypes);
              if (result != null && Modifier.isPublic(result.getAccessFlags())) {
                  return result;
              }
          }
        
          return findInterfaceMethod(name, parameterTypes);
        }
        
        private Method findInterfaceMethod(String name, Class<?>[] parameterTypes) {
          Object[] iftable = ifTable;
          if (iftable != null) {
              // Search backwards so more specific interfaces are searched first. This ensures that
              // the method we return is not overridden by one of it's subtypes that this class also
              // implements.
              for (int i = iftable.length - 2; i >= 0; i -= 2) {
                  Class<?> ifc = (Class<?>) iftable[i];
                  Method result = ifc.getPublicMethodRecursive(name, parameterTypes);
                  if (result != null && Modifier.isPublic(result.getAccessFlags())) {
                      return result;
                  }
              }
          }
        
          return null;
        }
        
        /**
        * Returns the method if it is defined by this class; {@code null} otherwise. This may return a
        * non-public member.
        *
        * @param name the method name
        * @param args the method's parameter types
        */
        @FastNative
        private native Method getDeclaredMethodInternal(String name, Class<?>[] args);
        

        ifTable的注解:

        /**
        * The interface table (iftable_) contains pairs of a interface class and an array of the
        * interface methods. There is one pair per interface supported by this class.  That
        * means one pair for each interface we support directly, indirectly via superclass, or
        * indirectly via a superinterface.  This will be null if neither we nor our superclass
        * implement any interfaces.
        *
        * Why we need this: given "class Foo implements Face", declare "Face faceObj = new Foo()".
        * Invoke faceObj.blah(), where "blah" is part of the Face interface.  We can't easily use a
        * single vtable.
        *
        * For every interface a concrete class implements, we create an array of the concrete vtable_
        * methods for the methods in the interface.
        */
        

        根据注解,我们知道ifTable是一个存储类实现的interface和其接口方法数组的,第0个元素存放接口类,第1个元素存放其方法数组,按这样接口类-接口方法数组的方式依次存放。

        以上,不难总结出这两个方法的作用,调用getMethod方法会遍历当前类和继承的父类、实现的接口中的所有方法,按照this.class、superClass、implInterface1、implInterface2这个顺序如果找到了就不往下找了,注意getSuperclass:

        public Class<? super T> getSuperclass() {
          // For interfaces superClass is Object (which agrees with the JNI spec)
          // but not with the expected behavior here.
          if (isInterface()) {
              return null;
          } else {
              return superClass;
          }
        }
        

        所以搜索顺序是首先搜索当前类中定义的,没找到则搜索直接父类中的方法,还没找到再搜索父类的父类中的方法,一直找到getSuperclass返回null(是interface或者Object),如果都没找到就去搜索其直接实现的接口中的方法,比如说当前class实现了interface1和interface2,则先去interface2搜索然后是interface1搜索,假如interface1是interface3的子接口,也不会去搜索interface3,因为interface1的getSuperclass是接口类型。注意,getMethod方法查找的方法必须是public方法。

        getDeclaredMethod方法会调用getDeclaredMethodInternal方法:

        /**
        * Returns the method if it is defined by this class; {@code null} otherwise. This may return a
        * non-public member.
        *
        * @param name the method name
        * @param args the method's parameter types
        */
        @FastNative
        private native Method getDeclaredMethodInternal(String name, Class<?>[] args);
        

        这是个native方法,根据注释,只会查找当前类中定义的方法,它可以是任何类型的权限修饰符修饰的,没有必须是public的限制。

      • getInstanceMethod

        获取当前类及其父类中的非静态方法,找到就返回:

        public Method getInstanceMethod(String name, Class<?>[] parameterTypes)
                throws NoSuchMethodException, IllegalAccessException {
            for (Class<?> c = this; c != null; c = c.getSuperclass()) {
                Method result = c.getDeclaredMethodInternal(name, parameterTypes);
                if (result != null && !Modifier.isStatic(result.getModifiers())) {
                    return result;
                }
            }
        
            return findInterfaceMethod(name, parameterTypes);
        }
        
      • getResourceAsStream
         public InputStream getResourceAsStream(String name) {
            name = resolveName(name);
            ClassLoader cl = getClassLoader();
            if (cl==null) {
                // A system class.
                return ClassLoader.getSystemResourceAsStream(name);
            }
            return cl.getResourceAsStream(name);
        }
        

        使用当前类的ClassLoader去加载资源输入流(如果存在的话)。

      • getResource
        public java.net.URL getResource(String name) {
            name = resolveName(name);
            ClassLoader cl = getClassLoader();
            if (cl==null) {
                // A system class.
                return ClassLoader.getSystemResource(name);
            }
            return cl.getResource(name);
        }
        
      • resolveName
        private String resolveName(String name) {
            if (name == null) {
                return name;
            }
            if (!name.startsWith("/")) {
                Class<?> c = this;
                while (c.isArray()) {
                    c = c.getComponentType();
                }
                String baseName = c.getName();
                int index = baseName.lastIndexOf('.');
                if (index != -1) {
                    name = baseName.substring(0, index).replace('.', '/')
                        +"/"+name;
                }
            } else {
                name = name.substring(1);
            }
            return name;
        }
        

        把该类的包名中的.换成/然后当成参数name的前缀,比如a.b.C这个class调用这个方法,传入参数是John,那返回的就是a/b/John。

  • 其他

    • getEnumConstants

      getEnumConstants()方法可以返回一个enum类定义的所有枚举值。

      示例:

      enum class Color {
          RED,
          GREEN,
          PURPLE,
          ORANGE,
          PINK
      }
      
      val clazz = Color::class.java
      clazz.enumConstants?.forEach {
          println(it)
      }
      

      输出:

      GREEN
      PURPLE
      ORANGE
      PINK
      Process finished with exit code 0
      
    • getComponentType

      getComponentType是作用于数组类的,会返回:

      /**
       * For array classes, the component class object for instanceof/checkcast (for String[][][],
       * this will be String[][]). null for non-array classes.
       */
      private transient Class<?> componentType;
      

      可见它会把多维数组脱一层。

      示例:

      val clazz = arrayOf(arrayOf(arrayOf<String>())).javaClass
      println(clazz.componentType)
      println(clazz.componentType?.componentType)
      println(clazz.componentType?.componentType?.componentType)
      

      输出:

      class [[Ljava.lang.String;
      class [Ljava.lang.String;
      class java.lang.String
      
      Process finished with exit code 0
      
    • isInstance(Object obj)

      判断obj是否是该类类型或其子类类型,内部调用isAssignableFrom判断。

    • isAssignableFrom(Class<?> cls)

      可转让的意思,判断cls是否是该类继承关系下的类型:

      public boolean isAssignableFrom(Class<?> cls) {
          if (this == cls) {
              return true;  // Can always assign to things of the same type.
          } else if (this == Object.class) {
              return !cls.isPrimitive();  // Can assign any reference to java.lang.Object.
          } else if (isArray()) {
              return cls.isArray() && componentType.isAssignableFrom(cls.componentType);
          } else if (isInterface()) {
              // Search iftable which has a flattened and uniqued list of interfaces.
              Object[] iftable = cls.ifTable;
              if (iftable != null) {
                  for (int i = 0; i < iftable.length; i += 2) {
                      if (iftable[i] == this) {
                          return true;
                      }
                  }
              }
              return false;
          } else {
              if (!cls.isInterface()) {
                  for (cls = cls.superClass; cls != null; cls = cls.superClass) {
                      if (cls == this) {
                          return true;
                      }
                  }
              }
              return false;
          }
      }
      
    • getEnclosingConstructor()、getEnclosingMethod()、getEnclosingClass()

      如果class分别是在类的构造方法里、普通方法里、类中定义的,则它们分别返回这个类的该构造方法、普通方法和该类,即内部类的定义场所。

      以getEnclosingConstructor()为例,源码是:

      public Constructor<?> getEnclosingConstructor() {
          if (classNameImpliesTopLevel()) {
              return null;
          }
          return getEnclosingConstructorNative();
      }
      
      private boolean classNameImpliesTopLevel() {
          return !getName().contains("$");
      }
      

      内部类的类名都是‘主类$内部类’的形式,所以判断如果不含有则说明不是。

      示例:

      package com.a.b;
      public class ClassDemo {
      
          public Object c;
      
          public ClassDemo( ) {
              class ClassA{ };
              c = new ClassA( );
          }
      
          public Object ClassAObject( ) {
              class ClassA{ };
              return new ClassA( );
          }
          
          class MyInnerClass{}
      
          public static void main(String[] args) {
              Class cls;
              cls = (new ClassDemo()).ClassAObject().getClass();
      
              System.out.print("Method = ");
              System.out.println(cls.getEnclosingMethod());
      
              Class cls2;
              cls2 = (new ClassDemo()).c.getClass();
      
              System.out.print("getEnclosingConstructor() = ");
              System.out.println(cls2.getEnclosingConstructor());
              
              Class cls3;
              cls3 = (new ClassDemo.MyInnerClass()).getClass();
      
              System.out.print("getEnclosingClass() = ");
              System.out.println(cls2.getEnclosingClass());
          }
      } 
      

      让我们来编译和运行上面的程序,这将产生以下结果:

      Method = public java.lang.Object ClassDemo.ClassAObject()
      getEnclosingConstructor() = public ClassDemo()
      getEnclosingClass() =class com.a.b.ClassDemo
      
      Process finished with exit code 0
      
    • getSimpleName()

      只返回类名,不包含包名路径等信息。

    • getTypeName

      主要在于数组类型的不同,示例:

      val clazz = arrayOf(arrayOf(arrayOf<String>())).javaClass
      println(clazz.simpleName)
      println(clazz.name)
      println(clazz.typeName)
      
      String[][][]
      [[[Ljava.lang.String;
      java.lang.String[][][]
      
      Process finished with exit code 0
      
    • getCanonicalName

      主要在于内部类的不同,示例:

      val clazz = Apple.EnclosingClass().javaClass
      println(clazz.canonicalName)
      println(clazz.simpleName)
      println(clazz.name)
      println(clazz.typeName)
      
      com.mph.rxseriers.reflect_test.Apple.EnclosingClass
      EnclosingClass
      com.mph.rxseriers.reflect_test.Apple$EnclosingClass
      com.mph.rxseriers.reflect_test.Apple$EnclosingClass
      
      Process finished with exit code 0
      
    • isLocalClass

      判断是不是LocalClass:

      public boolean isLocalClass() {
          return (getEnclosingMethod() != null || getEnclosingConstructor() != null)
                  && !isAnonymousClass();
      }
      

      可见LocalClass就是在方法内部活着构造方法内部定义的类。

    • isMemberClass

      MemberClass就是类中定义的内部类,判断的标准就是getDeclaredClass()是不是空。

    • getClasses

      返回所有继承的类及其DeclaredClass(如果他是内部类的话):

      @CallerSensitive
      public Class<?>[] getClasses() {
          List<Class<?>> result = new ArrayList<Class<?>>();
          for (Class<?> c = this; c != null; c = c.superClass) {
              for (Class<?> member : c.getDeclaredClasses()) {
                  if (Modifier.isPublic(member.getModifiers())) {
                      result.add(member);
                  }
              }
          }
          return result.toArray(new Class[result.size()]);
      }
      
    • getAnnotations()、getAnnotation(Class annotationClass)、getAnnotationsByType(Class annotationClass)

      getAnnotation(Class annotationClass)会返回一个annotationClass类型的注解类;

      getAnnotations返回所有的注解;

      getAnnotationsByType返回指定类型的所有注解。

      它们都会判断如果注解本身有Inherit注解修饰则会包含其父注解的相关注解。

  • Field类

    Field继承自AccessibleObject,实际上Method和Constructor也是间接的继承自AccessibleObject(通过Executable),通过它的setAccessible取得修改权限达到动态赋值和调用的目的。另外Field实现了Member接口,同样类中的元素都会实现它,getModifiers、getName、getDeclaringClass都是它内部的方法。有两个常量PUBLIC和DECLARED,分别表示包括所有的public集合(包括属性和方法)、只在当前类定义的(无权限限制)。

    还有一个isSynthetic()方法,经查阅,它是在外部类调用其内部类的时候编译期间用来达到调用private元素时创建的元素的类型。

    • getType、getGenericType

      示例类:

      class Orange<T> extends Fruit {
          public String orangeSize ;
          public T aaa ;
          public List<T> bbb ;
          public T[] ccc ;
          public List<? extends T> ddd ;
      }
      

      getType结果:

      orangeSize : class java.lang.String
      aaa : class java.lang.Object
      bbb : interface java.util.List
      ccc : class [Ljava.lang.Object;
      ddd : interface java.util.List
      

      getGenericType结果:

      orangeSize : class java.lang.String
      aaa : T
      bbb : java.util.List<T>
      ccc : T[]
      ddd : java.util.List<? extends T>
      
    • 一系列get和set方法用于反射调用

  • Executable

    Method类主要是invoke方法,Constructor主要是newInstance方法创建实例,获取他们的信息主要来自于它们的父类Executable。

    • getParameterTypes、getGenericParameterTypes

      和Field的getType、getGenericType一样,getParameterTypes获取方法的参数类型Class数组,getGenericParameterTypes获取方法的参数类型Type数组。

    • getParameterCount

      获取方法参数个数。

    • getParameters

      获取方法参数数组,和getParameterTypes不同的是,这个会返回Parameter类型的数组,不止参数类型。

      private final String name;
      private final int modifiers;
      private final Executable executable;
      private final int index;
      
      public Type getParameterizedType() {
          Type tmp = parameterTypeCache;
          if (null == tmp) {
              tmp = executable.getAllGenericParameterTypes()[index];
              parameterTypeCache = tmp;
          }
      
          return tmp;
      }
      

      可以看到内部获取参数类型也是通过executable的方法。

      同样作用的还有个getType:

      public Class<?> getType() {
          Class<?> tmp = parameterClassCache;
          if (null == tmp) {
              tmp = executable.getParameterTypes()[index];
              parameterClassCache = tmp;
          }
          return tmp;
      }
      

      这个会返回class类型的,就像前面说到的getType和getGenericType的不同一样。

    • getExceptionTypes、getGenericExceptionTypes

      同样的返回异常类型数组,一个是class,一个是Type。

    • getTypeParameters

      返回方法前面的泛型参数,比如public <T,F> void getOO(){},会返回T和F的TypeVariable类型,调用TypeVariable的name方法或typeName方法就会得到T和F。

  • Type

    Type接口有四个子接口GenericArrayType、ParameterizedType、TypeVariable、WildcardType和一个实现类Class,四个子接口都是和泛型相关的。

    关于什么样的类型对应他们,下面一个示例可以说明:

    public <F,G>  T  getOrange(int dd , String a , T b, F c, T[] d, List<T> e, List<? extends F> f, G g){
        return b;
    }
    
    fun testMethod() {
        val clazz = Orange::class.java
        clazz.declaredMethods.forEach {
            if(it.name == "getOrange") {
                it.genericParameterTypes.forEach {geType->
                    when{
                        geType is GenericArrayType->{
                            println("${geType.typeName} is GenericArrayType")
    
                        }
                        geType is GenericDeclaration->{
                            println("${geType.typeName} is GenericDeclaration")
    
                        }
                        geType is ParameterizedType->{
                            println("${geType.typeName} is ParameterizedType")
    
                        }
                        geType is WildcardType->{
                            println("${geType.typeName} is WildcardType")
    
                        }
                        geType is TypeVariable<*> ->{
                            println("${geType.typeName} is TypeVariable")
    
                        }
                    }
                }
            }
        }
    
    }
    

    输出结果是:

    int is GenericDeclaration
    java.lang.String is GenericDeclaration
    T is TypeVariable
    F is TypeVariable
    T[] is GenericArrayType
    java.util.List<T> is ParameterizedType
    java.util.List<? extends F> is ParameterizedType
    G is TypeVariable
    
    Process finished with exit code 0
    

    这里有一个GenericDeclaration类型的,它不属于Type子类,直接继承自AnnotatedElement,基本类型、String和自定义的非泛型类都是属于这个类型。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值