java 反射和泛型实践

2,152 阅读9分钟

反射 反射是框架设计的灵魂 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。 每一个类对应一个Class对象。所有的反射都是从Class对象开始的。运行时将class文件读入内存,并创建对应的class对象。 反射就是把java类中的所有成分映射为不同的对象。Class由JVM创建,然后可以从Class对象获取需要的类成员对象,包括但不限于Method、Field、Constructor等。类里面写的所有东西都可以通过class获取(前提是信息可以保存到运行时,比如有些注解不会保留到运行期,可以通过这一点绕过泛型检查)。

/**

  • 定义person类 */ public class Person {

      public Person(){
         System.out.println("Person类无参数构造");
      }
       public Person(int a,int b,String s){
         System.out.println("Person类有参数构造:a:"+a+" b:"+b+" s:"+s);
      }
        private Person(int a){
          System.out.println("Person类有参数   私有 构造:a:"+a);
    }
     }
    

public class ReflectTest { public static void main(String[] args) throws ClassNotFoundException {

    //第1种方式获取Class对象

    Person p1=new Person();
    Class cc=p1.getClass();
    System.out.println(cc);

    //第2种方式获取Class对象
    Class cc2=Person.class;
    System.out.println(cc2);
    //第3种方式获取Class对象
    Class cc3=Class.forName("com.test.demo.Person");//全类名
    System.out.println(cc3);

    System.out.println(cc==cc2);
    System.out.println(cc2==cc3);
}
}


//输出结果
class com.test.demo.Person
class com.test.demo.Person
class com.test.demo.Person
true
true

————————————————

public class ReflectTest {
  public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException,   
  InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {

    Class clazz=Person.class;
    //获取Person类所有 公共 构造
    Constructor [] conarr=clazz.getConstructors();

    for(Constructor con :conarr){
        System.out.println(con);
    }
    //获取指定构造方法
    //无参数
    Constructor cc=clazz.getConstructor();
    Object oo=cc.newInstance();

    //有参数
    Constructor cc2=clazz.getConstructor(int.class,int.class,String.class);
    Object oo2=cc2.newInstance(1,2,"haha");

    //获取私有构造方法
    Constructor cc3=clazz.getDeclaredConstructor(int.class);
    //暴力访问
    cc3.setAccessible(true);
    Object oo3=cc3.newInstance(1);

    clazz.newInstance();//直接获取空参数构造,必须是public
}
}

输出结果
public com.test.demo.Person()
public com.test.demo.Person(int,int,java.lang.String)
Person类无参数构造
Person类有参数构造:a:1 b:2 s:haha
Person类有参数   私有 构造:a:1
Person类无参数构造

 public String name="smt";
private String idcard="1001u09t";

@Override
public String toString() {
    // TODO Auto-generated method stub
    return "name:"+name+"  idcard:"+idcard;
}

————————————————

public class ReflectTest {
    public static void main(String[] args) throws ClassNotFoundException,
        NoSuchMethodException, SecurityException, InstantiationException,
        IllegalAccessException, IllegalArgumentException,
        InvocationTargetException, NoSuchFieldException {

    Class clazz = Person.class;
    Object obj = clazz.newInstance();
    // 获取Person类所有 公共 成员变量
    Field[] fields = clazz.getFields();
    for(Field s:fields){
        System.out.println(s);
    }

    Field field=clazz.getField("name");

    field.set(obj, "haha");

    System.out.println(obj);

    Field field2=clazz.getDeclaredField("idcard");
    field2.setAccessible(true);
    field2.set(obj, "123456");
    System.out.println(obj);
}
}

输出结果
Person类无参数构造
public java.lang.String com.test.demo.Person.name
name:haha  idcard:1001u09t
name:haha  idcard:123456

获取成员方法 为Person类增加成员方法

public void show(){
    System.out.println("show 空参数");
}
public void show(int a){
    System.out.println("show   a:"+a);
}   
private void show(String s){
    System.out.println("show   s:"+s);
}


public class ReflectTest {
      public static void main(String[] args) throws ClassNotFoundException,
        NoSuchMethodException, SecurityException, InstantiationException,
        IllegalAccessException, IllegalArgumentException,
        InvocationTargetException, NoSuchFieldException {


    Class clazz = Person.class;
    Object obj = clazz.newInstance();
    // 获取Person类所有 公共 成员方法
    Method [] methods=clazz.getMethods();

    for(Method m:methods){
        System.out.println(m);
    }

    Method m=clazz.getMethod("show");
    m.invoke(obj);

    Method m1=clazz.getMethod("show",int.class);
    m1.invoke(obj,1);

    Method m2=clazz.getDeclaredMethod("show",String.class);
    m2.setAccessible(true);
    m2.invoke(obj,"smt");
}
}


输出结果:
Person类无参数构造
public java.lang.String com.test.demo.Person.toString()
public void com.test.demo.Person.show(int)
public void com.test.demo.Person.show()
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
show 空参数
show   a:1
show   s:smt

————————————————

泛型 泛型是指参数化形式,在编程时将具体的类型写为类型参数,然后在调用的时候传入具体的类型。 比如List,如果不用泛型,那么可以向其传入任意类型的参数,get()的时候再做类型转换。如果使用了泛型,所有的传入的类型会在编译时做类型检查,如果不符合泛型定义的类型,编译期就会报错。 泛型只在编译期生效。在运行时,所有的泛型会被擦除,同时添加类型检查和类型转化。可以在生成的class文件中查看。 泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法。

泛型类 最典型的是java中的容器类,List、Map、Set等。

public class Generic<T>{ 
   //key这个成员变量的类型为T,T的类型由外部指定  
   private T key;
 public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定
    this.key = key;
}
public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定
    return key;
}
}

这里定义了一个泛型类Generic,T表示泛型参数。Generic g = new Generic()表示泛型参数为String。这时编译器会检查传入的类型是否为String。 如果定义对象时不设定泛型参数,类似Generic g = new Generic(),这时泛型检查不起作用,可以传入任何类型。

泛型接口 类似于泛型类

//定义一个泛型接口
public interface Generator<T> {
public T next();
}

这里定义了一个泛型接口。使用情况分为两种:未传入泛型实参和传入泛型实参。

未传入泛型实参 /**

  • 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中

  • 即:class FruitGenerator implements Generator{

  • 如果不声明泛型,如:class FruitGenerator implements Generator,编译器会报错:"Unknown class" */

      class FruitGenerator<T> implements Generator<T>{
      @Override
        public T next() {
     return null;
        }
      }
    

该实现类的使用与泛型类相同

传入泛型实参 /**

  • 传入泛型实参时:

  • 定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口Generator

  • 但是我们可以为T传入无数个实参,形成无数种类型的Generator接口。

  • 在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型

  • 即:Generator,public T next();中的的T都要替换成传入的String类型。 */

    public class FruitGenerator implements Generator { private String[] fruits = new String[]{"Apple", "Banana", "Pear"}; @Override public String next() { Random rand = new Random(); return fruits[rand.nextInt(3)]; } }

这里泛型参数被设为。实现类的使用与普通类一致。

泛型方法 泛型方法,是在调用方法的时候指明泛型的具体类型 。

/**

  • 泛型方法的基本介绍
  • @param tClass 传入的泛型实参
  • @return T 返回值为T类型
  • 说明:
  • 1)public 与 返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。
    
  • 2)只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
    
  • 3)<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
    
  • 4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。
    

*/ public T genericMethod(Class tClass)throws InstantiationException , IllegalAccessException{ T instance = tClass.newInstance(); return instance; } 这里在方法的public和返回值之间使用了,表示该方法为泛型方法。T会在使用时被替换为相应的类,比如Object obj = genericMethod(Class.forName("com.test.test"))。 泛型方法可以出现在任何地方,泛型方法的泛型参数可以覆盖泛型类的同名泛型参数。 静态方法无法访问类上定义的泛型,如果静态方法使用泛型,必须在方法层面进行声明。

如果一个方法接收一个泛型对象作为参数,同时不确定传入的泛型对象的具体类型。这里必须使用泛型参数通配符<?>。因为不同的泛型参数并不兼容,就算之间有继承关系也不兼容。

泛型上下边界 在定义泛型时,可以使用或者<?extend Object>来限定泛型的类型。 表示传入的泛型类型必须为指定类型或其子类型。除了extends,还有super,表示传入的泛型类型必须为指定类型或其父类型。

首先我们定义A、B、C、D四个类,他们的关系如下 class A {} class B extends A {} class C extends B {} class D extends C {} 复制代码不指明泛型类型 //以下代码均编译通过 List list = new ArrayList(); //不指明泛型类型,泛型默认为Object类型,故能往里面添加任意实例对象 list.add(new A()); list.add(new B()); list.add(new C()); //取出则默认为Object类型 Object o = list.get(0)

无边界通配符 ? 首先我们要明白一个概念,通配符?意义就是它是一个未知的符号,可以是代表任意的类。 //我们发现,这样写编译不通过,原因很简单,泛型不匹配,虽然B继承A List listA = new ArrayList();

//以下5行代码均编译通过 List<?> list; list = new ArrayList(); list = new ArrayList(); list = new ArrayList(); list = new ArrayList();

Object o = list.get(0); //编译通过 list.add(new A()); //编译不通过 list.add(new B()); //编译不通过 list.add(new C()); //编译不通过 list.add(new D()); //编译不通过

无边界通配符 ? 能取不能存。这个好理解,因为编译器不知道?具体是啥类型,故不能存;但是任意类型都继承于Object,故能取,但取出默认为Object对象。

上边界符 ?extends 继续上代码 List<? extends C> listC;

listC = new ArrayList(); //编译不通过

listC = new ArrayList(); //编译不通过

listC = new ArrayList(); //编译通过

listC = new ArrayList(); //编译通过

C c = listC.get(0); //编译通过

listC.add(new C()); //编译不通过

listC.add(new D()); //编译不通过

复制代码知识点:

上边界符 ? extends 只是限定了赋值给它的实例类型(这里为赋值给listC的实例类型),且边界包括自身。

上边界符 ? extends 跟 ? 一样能取不能存,道理是一样的,虽然限定了上边界,但编译器依然不知道 ?

是啥类型,故不能存;但是限定了上边界,故取出来的对象类型默认为上边界的类型

下边界符 ?super

List<? super B> listB;

listB = new ArrayList(); //编译通过

listB = new ArrayList(); //编译通过

listB = new ArrayList(); //编译不通过

listB = new ArrayList(); //编译不通过

Object o = listB.get(0); //编译通过

listB.add(new A()); //编译不通过

listB.add(new B()); //编译通过

listB.add(new C()); //编译通过

listB.add(new D()); //编译通过

下边界符 ?super,跟上边界符一样,只是限定了赋值给它的实例类型,也包括边界自身

下边界符 ?super 能存能取,因为设定了下边界,故我们能存下边界以下的类型,当然也包括边界自身;然而取得时候编译器依然不知道 ? 具体是什么类型,故取出默认为Object类型。

类型擦除 List listA = new ArrayList();

List listB = new ArrayList();

System.out.println(listA.getClass() == listB.getClass()); //输出true

public class Response<T> {
   private T data;
private int code;
private String msg;
//省略get/set方法
}

  //这里我们直接传递一个Type类型
  public static <T> T fromJson(String result, Type type) {
    try {
        return new Gson().fromJson(result, type);
    } catch (Exception ignore) {
        return null;
    }
}