java 泛型注射与反射

注解

java注解(Annotation)又称为java标注,注解是JDK5.0引入的一种能在代码中加入元数据的机制,不直接影响程序的运行,通过工具或框架,在编译时、类加载时或运行时处理这些元数据,来改变程序的行为或提供额外的功能。

注解声明

声明一个注解类型

下面就是@MyAnnotation注解声明

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface MyAnnotation {
    String value();
}

元注解

什么是元注解,元注解就是注解的注解,如上面@MyAnnotation注解,其中ElementType.METHOD表示只能用于在方法上的注解,而@Target(ElementType.METHOD)用于在注解上的注解,也就是所谓的元注解。

  1. @Target

    注解标注另一个注解,用于限制应用注解的java元素类型。常见的值为:

    • ElementType.Type 类、接口(注解类型)或枚举声明
    • ElementType.FIELD 字段或属性声明
    • ElementType.METHOD 方法声明
    • ElementType.PARAMETER 方法的参数声明
    • ElementType.CONSTRUCTOR 构造函数声明
    • ElementType.LOCAL_VARIABLE 局部变量声明
    • ElementType.ANNOTATION_TYPE 注解类型声明
    • ElementType.PACKAGE 包声明
  2. @Retention

    定义注解的保留策略,常见的值为:

    • RetentionPolicy.SOURCE 注解只保留在源码中,也就是在编译时,编译后丢弃。(常用技术APT)
    • RetentionPolicy.CLASS 注解在编译时由编译器保留,但JVM运行时不可见。(常用技术字节码增强)
    • RetentionPolicy.RUNTIME 注解运行时也存在,由JVM保留。(常用技术反射)

    三者之间关系 .java文件 -> .class文件 -> 内存中的字节码,SOURCE包含于CLASS,CLASS包含于RUNTIME

  3. @Documented 用于被javadoc工具提取成文档

  4. @Inherited 表示允许子类继承父类中定义的注解

反射

一般想调用一个对象内部的属性或者方法,我们会去通过new 实例化获取此类的引用,再使用这个类的引用进行操作。

如果一开始不知道要使用什么对象,就不会去实例化创建对象,这时可以通过JDK提供的的反射API进行反射调用。反射就是在运行时,对于任意一个类都能知道此类的所有属性、所有方法;对于任意一个对象都能调用它的所有属性、所有方法,并能修改,我称为全知全能(hh,小说看多了)。

什么是Class

知道任意一个类,对象的属性,方法,其中类、对象不就是class吗,想要反射,那就得从class类开始,一个class类,用来封装了当前对象所对应的类的信息,通过这个class类,我们就能获取他有哪些属性,方法,构造,实现了什么接口等等

获取Class对象

想开始操作了,那如何获取Class类了

  1. Class.forName(com.example.application.Apple) 第一种通过调用Class.forName(String className) 传入全类名获取Class对象。

  2. Class<Integer> intClass = int.class;
    Class<Integer> integerClass = Integer.TYPE;

    第二种,通过类名.class获取。

  3. Class<? extends Food> foodClass= new Food().getClass(); 第三种,通过实例对象获取。

判断是否为某个类的实例

public native boolean isInstance(Object obj)

判断是否为某个类的类型

public boolean isAssignableFrom(Class<?> cls)

创建Class类描述类的实例

//该native方法用于创建实例
public native T newInstance()//Class类
//方式一
Class<?> intClass = int.class;
Object num = intClass.newInstance();
//方式二,用于有构造器,并想指定构造器,实例构造类的
Class<?> stringClass = String.class;
Constructor<?> constructor = stringClass.getConstructor(String.class);
Object str = constructor.newInstance("hello");

获取构造器信息

Constructor<T> getConstructor(Class<?>... parameterTypes) -- 使用特殊参数,获取所有自己与父类的public修饰的构造函数
Constructor<?>[] getConstructors() -- 获取所有自己与父类的public修饰的构造函数
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) -- 使用特殊参数,获取自己的所有构造函数(不论修饰符)   
Constructor<?>[] getDeclaredConstructors() -- 获取自己的所有构造函数(不论修饰符)

Class源码中方法的规律:

无Declared,获取所有自己与父类的public修饰的**

有Declared,获取所有自己的**(不论修饰符)

无后缀s,可通过特殊参数,指定所有的**

有后缀s,获取所有的**

获取成员变量(字段)信息

Field getField(String name)//Class类

获取成员方法,并调用成员方法

Method getMethod(String name, Class<?>... parameterTypes)//Class类
Object invoke(Object obj, Object... args)//通过获取的方法调用这个方法 Method类   

通过反射创建数组

public static Object newInstance(Class<?>... parameterTypes,int length);//Array类

反射获取泛型真实类型

当我们对一个泛型类进行反射时,想要获取泛型中的真实数据类型,来完成json反序列化操作,就需要通过Type体系来完成,Type接口中包含了一个实现类(Class)和四个实现接口,分别是:

  • TypeVariable
    • 泛型类型变量,可以获取泛型上下限等信息。
  • ParameterizedType
    • 具体的泛型类型,可以获得元数据中泛型签名类型(泛型真实类型)。
  • GenericArrayType
    • 当需要描述的类型是泛型类的数组时,比如List[]、Map[],此接口会作为Type的实现。
  • WildcardType
    • 通配符泛型,获得上下限信息。

使用案例:

  1. fastjson+okhttp请求

    public class HttpUtil {
        public static <T> void response(Back<T> back){
            //模拟一串序列化数据
            String data = "{\"code\":200,\"data\":{\"name\":\"apple\"}}";
            //反序列化
            T o = JSON.parseObject(data, back.getType());
            back.onSuccess(o);
        }
    //    public static <T> void response(Back<T> back, TypeReference<T> typeRef){
    //        //模拟一串序列化数据
    //        String data = "{\"code\":200,\"data\":{\"name\":\"apple\"}}";
    //        //反序列化
    //        T o = JSON.parseObject(data, typeRef);
    //        back.onSuccess( o);
    //    }
        
        public abstract static class Back<T>{
            Type type;
            public Back() {
                //反射获取类的泛型信息
                Type superclass = getClass().getGenericSuperclass();
                //获取到泛型的具体信息,也就是泛型真是类型
                ParameterizedType parameterizedType = (ParameterizedType) superclass;
                //泛型可能不只一个所以取[0]位,<T,E,K>
                type = parameterizedType.getActualTypeArguments()[0];
            }
    
            public Type getType() {
                return type;
            }
    
            abstract void onSuccess(T data);
        }
    }
    
    public class test {
        public static void main(String[] args) {
    //        HttpUtil.response((Res<Fruit> data) -> {
    //            System.out.println(data.getClass());
    //            System.out.println(data.code);
    //            System.out.println(data.data.name);
    //        }, new TypeReference<>(){});
    
            //匿名内部类实现Back抽象类,onSuccess作为回调函数
            HttpUtil.response(new HttpUtil.Back<Res<Fruit>>() {
                @Override
                void onSuccess(Res<Fruit> data) {
                    System.out.println(data.getClass());
                    System.out.println(data.code);
                    System.out.println(data.data.name);
                }
            });
        }
    }
    

    其中注释的代码中创建new TypeReference<Res<Fruit>>(){} 为什么需要花括号。

    因为没有花括号创建的泛型对象,它在运行期,会被泛型擦除成Object,Object类型就无法让fastjson知道具体要反序列是那个对象。

    而加入花括号(抽象类或接口)就会动态创建子对象,在子对象中就会具现化泛型信息,该signature信息就会记录到Class元数据中。

    再上述代码中Back就为一个抽象类,构造函数中获取泛型的信息,但构造的时候Class元数据中还未有signature泛型信息,所以需要实现抽象类,将signature泛型信息记录到Class元数据中。

  2. Android Intent 传值(注解+反射)

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectExtra {
    String value() default "";
}

public class InjectUtil {
    public static void initExtra(Activity activity){
        Intent intent = activity.getIntent();
        Bundle extras = intent.getExtras();
        if(extras==null) return;

        Class<? extends Activity> aClass = activity.getClass();
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field field : declaredFields) {
            //判断该属性的注解是否为@InjectExtra
            if(field.isAnnotationPresent(InjectExtra.class)){
                InjectExtra extra = field.getAnnotation(InjectExtra.class);
                //获取注解中的值
                String key = extra.value();
                if(key==null || key.isEmpty()) key = field.getName();
                
                if(extras.containsKey(key)){
                    Object obj = extras.get(key);

                    Class<?> componentType = field.getType().getComponentType();
                    //特殊处理Parcelable的实现类
                    if(field.getType().isArray() &&
                            Parcelable.class.isAssignableFrom(componentType)){
                        Objects[] objs = (Objects[]) obj;
                        Objects[] objects = Arrays.copyOf(objs,objs.length,(Class<? extends Objects[]>) field.getType());
                        obj = objects;
                    }
                    //设置true,可修改私有属性
                    field.setAccessible(true);

                    try {
                        //参数一,那一对象的属性赋值,参数而:值
                        field.set(activity,obj);
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }

    }
}

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(this,MainActivity2.class);
        intent.putExtra("name","apple");
        intent.putExtra("boy",false);
        intent.putExtra("food","orange");
        intent.putExtra("id",555);
        startActivity(intent);
    }
}

public class MainActivity2 extends AppCompatActivity {

    @InjectExtra
    String name;

    @InjectExtra("boy")
    Boolean isBoy;

    @InjectExtra("food")
    String fruit;

    @InjectExtra
    int id;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main2);
        InjectUtil.initExtra(this);
        Toast.makeText(this, "name: "+name+" \nisBoy: "+isBoy+" \nfruit: "+fruit+" \nid: "+id, Toast.LENGTH_LONG).show();
    }
}

上述就是一个简单的注解加反射,实现快速注入Intent传入的参数

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值