java筑基.泛型,反射,注解-利用注解加反射练习

Posted 六道对穿肠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java筑基.泛型,反射,注解-利用注解加反射练习相关的知识,希望对你有一定的参考价值。

文章目录

泛型:

把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型

泛型类


/*
    1:把泛型定义在类上
    2:类型变量定义在类上,方法中也可以使用
 */
public class ObjectTool<T> 
    private T obj;

    public T getObj() 
        return obj;
    

    public void setObj(T obj) 
        this.obj = obj;
    

  • 测试代码:

    public static void main(String[] args) 
        //创建对象并指定元素类型
        ObjectTool<String> tool = new ObjectTool<>();

        tool.setObj(new String("字符串"));
        String s = tool.getObj();
        System.out.println(s);


        //创建对象并指定元素类型
        ObjectTool<Integer> objectTool = new ObjectTool<>();
        /**
         * 如果我在这个对象里传入的是String类型的,它在编译时期就通过不了了.
         */
        objectTool.setObj(10);
        int i = objectTool.getObj();
        System.out.println(i);
    

用户想要使用哪种类型,就在创建的时候指定类型。使用的时候,该类就会自动转换成用户想要使用的类型了。

泛型方法

定义泛型方法…泛型是先定义后使用的


    //定义泛型方法..
    public <T> void show(T t) 
        System.out.println(t);

    

  • 测试代码:

用户传递进来的是什么类型,返回值就是什么类型了

    public static void main(String[] args) 
        //创建对象
        ObjectTool tool = new ObjectTool();

        //调用方法,传入的参数是什么类型,返回值就是什么类型
        tool.show("hello");
        tool.show(12);
        tool.show(12.5);

    

泛型接口


/*
    把泛型定义在接口上
 */
public interface Inter<T> 
    public abstract void show(T t);



子类明确泛型类的类型参数变量

/**
 * 子类明确泛型类的类型参数变量:
 */

public class InterImpl implements Inter<String> 
    @Override
    public void show(String s) 
        System.out.println(s);

    


子类不明确泛型类的类型参数变量

当子类不明确泛型类的类型参数变量时,外界使用子类的时候,也需要传递类型参数变量进来,在实现类上需要定义出类型参数变量


/**
 * 子类不明确泛型类的类型参数变量:
 *      实现类也要定义出<T>类型的
 *
 */
public class InterImpl<T> implements Inter<T> 

    @Override
    public void show(T t) 
        System.out.println(t);

    


测试代码:

    public static void main(String[] args) 
        //测试第一种情况
        //Inter<String> i = new InterImpl();
        //i.show("hello");

        //第二种情况测试
        Inter<String> ii = new InterImpl<>();
        ii.show("100");

    

  • 实现类的要是重写父类的方法,返回值的类型是要和父类一样的!
  • 类上声明的泛形只对非静态成员有效

限定类型变量


public class ArrayAlg

    //表示传入两个泛型,但是需要有compareTo方法 如何实现 使用限定类型变量
    //public static <T> T min(T a,T b)
    //    if(a.compareTo(b)>0) return a : else return b;
    // 
    
    //让 T extends Comparable
    public static <T extends Comparable> T min(T a,T b)
       if(a.compareTo(b)>0) return a : else return b;
     
     
     
     //可以使用& 跟上接口  也可以extneds类  但是类有且只有一个,接口可以有很多
     public static <T extends ArrayList & Comparable> T min(T a,T b)
       if(a.compareTo(b)>0) return a : else return b;
     

通配符泛型

?是通配符,泛指所有类型

一般用于定义一个引用变量,这么做的好处是,如下所示,定义一个sup的引用变量,就可以指向多个对象。

SuperClass<?> sup = new SuperClass<String>("lisi");

sup = new SuperClass<People>(new People());

sup = new SuperClass<Animal>(new Animal());

//若不用?,用固定的类型的话,则:

SuperClass<String> sup1 = new SuperClass<String>("lisi");

SuperClass<People> sup2 = new SuperClass<People>("lisi");

SuperClass<Animal> sup3 = new SuperClass<Animal>("lisi");

//这就是?通配符的好处。

注解

注解本身没有意义,单独的注解就是一种注释,他需要结合其他如反射,插桩的时候才有作用等技术才有意义.

定义: 使用@interface 关键字声明.

例如:


@Target(ElementType.FIELD)// 属性
@Retention(RetentionPolicy.RUNTIME)// 运行时
public @interface InjectPresenter 


或者


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface TestClassAnnotation 
    String id();
//    String value();
    //如果这里不是value 是id那么调用的时候需要id = "22"  value比较特殊
 



  • @Target(ElementType.TYPE,ElementType.METHOD) 也可以指明多个 在类上注解,在方法上也行

元注解

元注解顾名思义我们可以理解为注解的注解,它是作用在注解中,方便我们使用注解实现想要的功能。元注解分别有

  1. @Retention、 保留级别 只有三个数值 是只在代码中(SOURCE),还是编入class文件中(CLASS),或者是在运行时可以通过反射访问(RUNTIME) SOURCE<CLASS<RUNTIME CLASS包含了SOURCE RUNTIME包含了SOURCE和CLASS
  2. @Target、英文意思是目标,这也很容易理解,使用@Target元注解表示我们的注解作用的范围就比较具体了,可以是类,方法,方法参数变量等,同样也是通过枚举类ElementType表达作用类型
  3. @Document、 的英文意思是文档。它的作用是能够将注解中的元素包含到 Javadoc 中去。
  4. @Inherited和 英文意思是继承,但是这个继承和我们平时理解的继承大同小异,一个被@Inherited注解了的注解修饰了一个父类,如果他的子类没有被其他注解修饰,则它的子类也继承了父类的注解。
  5. @Repeatable(JDK1.8加入)五种。Repeatable使用场景:
    在需要对同一种注解多次使用时,往往需要借助@Repeatable。

Target的英文意思是目标,这也很容易理解,使用@Target元注解表示我们的注解作用的范围就比较具体了,可以是类,方法,方法参数变量等,同样也是通过枚举类ElementType表达作用类型

  • @Target(ElementType.TYPE) 作用接口、类、枚举、注解
  • @Target(ElementType.FIELD) 作用属性字段、枚举的常量
  • @Target(ElementType.METHOD) 作用方法
  • @Target(ElementType.PARAMETER) 作用方法参数
  • @Target(ElementType.CONSTRUCTOR) 作用构造函数
  • @Target(ElementType.LOCAL_VARIABLE)作用局部变量
  • @Target(ElementType.ANNOTATION_TYPE)作用于注解(@Retention注解中就使用该属性)
  • @Target(ElementType.PACKAGE) 作用于包
  • @Target(ElementType.TYPE_PARAMETER) 作用于类型泛型,即泛型方法、泛型类、泛型接口 (jdk1.8加入)
  • @Target(ElementType.TYPE_USE) 类型使用.可以用于标注任意类型除了 class (jdk1.8加入)
    一般比较常用的是ElementType.TYPE类型

注解的应用场景

根据注解的保留级别不同,对注解的使用自然存在不同的场景.

级别技术说明
SOURCE(源码)APT技术在编译器能够获取注解与注解声明的类包括勒种所有成员信息,一般用于生成额外的辅助类
CLASS(字节码)字节码增强/插桩在编译出Class后,通过修改Class数据以实现修改代码逻辑目的. 对于是否需要修改的区分活修改为不同逻辑的判断可以使用注解
RUNTIME(运行时)反射在程序运行期间,通过反射技术动态获取注解

SOURCE<CLASS<RUNTIME CLASS包含了SOURCE RUNTIME包含了SOURCE和CLASS

ide也提供了IntDef注解(元注解),提供语法检查 ide插件实现

IntDef举例:

//设置图片
public  static void setDrawable(int id)
    


public static void main(String... args)
    setDrawalbe(111)


setDrawalbe 本身应该传入资源图片但是现在传了随手写的int类型.这里可以使用ide提供的注解 @DrawableRes 资源

//设置图片
public  static void setDrawable(@DrawableRes int id)
    


public static void main(String... args)
    //setDrawalbe(111) //如果还随手写ide就会提示

@DrawableRes 是 androidX 定义好的语法检测规则

apt 注解处理器

反射:

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

作用: 能过做到一般做不到的事情. 使用场景: 插件式换肤,插件式开发 apk

反射练习: demo地址

注解+反射练习

通过注解+反射代替activity 通过Intent 传递参数跳转后的 getIntent的方法

比如 A Activity 携带参数

                Intent intent = new Intent();
                intent.setClass(MainActivity.this, JumpActivity.class);
                intent.putExtra("name", "testInject");
                intent.putExtra("age", 18);
                startActivity(intent);

在B Activity只需要如下做就可以自动赋值

 @Autowired("name")
    String mString;

    @Autowired("age")
    int age;
    

利用反射实现自动注入

mString = getintent.getStringExtra("name");
age = getintent.getIntExtra("age")

具体实现代码:

第一个Activity

package com.open.testc;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity 

    // Used to load the 'native-lib' library on application startup.
    static 
        System.loadLibrary("native-lib");
    

    private TextView sampleText;
    private Button btJump;


    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        sampleText = (TextView) findViewById(R.id.sample_text);
        btJump = (Button) findViewById(R.id.bt_jump);

        // Example of a call to a native method
        sampleText.setText(stringFromJNI());
        btJump.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View view) 
                Intent intent = new Intent();
                intent.setClass(MainActivity.this, JumpActivity.class);
                intent.putExtra("name", "testInject");
                intent.putExtra("age", 18);
                startActivity(intent);
            
        );
    

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();

第二个Activity

package com.open.testc;

import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import com.open.testc.inject.Autowired;

public class JumpActivity extends AppCompatActivity 

    private static final String TAG = "JumpActivtiy";
    @Autowired("name")
    String mString;

    @Autowired("age")
    int age;

    private TextView tvResult;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        InjectUtil.InjectView(this);
        setContentView(R.layout.activity_jump);
//        Bundle bundleExtra = getIntent().getBundleExtra();
        Log.d(TAG, "自动装配获取 onCreate: name = " + mString);
        Log.d(TAG, "自动装配获取 onCreate: age = " + age);

        tvResult = (TextView) findViewById(R.id.tv_result);

        tvResult.setText("name = " + mString + " age = " + age);
    

注入工具类:

package com.open.testc;

import android.app.Activity;
import android.os.Bundle;
import android.os.Parcelable;
import android.text.TextUtils;

import com.open.testc.inject.Autowired;

import java.lang.reflect.Field;
import java.util.Arrays;

/**
 * Created by liuml on 2021/4/14 19:40
 */
public class InjectUtil 

    public static void InjectView(Activity activity) 
        Class<? extends Activity> aClass = activity.getClass();
        try 
            //1.遍历这个activity里面所有的GetIntentField 注解
            //getDeclaredFields  和 getFields区别  getDeclaredFields()返回Class中所有的字段,包括私有字段
//        getFields  只返回公共字段,即有public修饰的字段。
            Field[] declaredFields = aClass.getDeclaredFields();
            Bundle bundle = activity.getIntent().getExtras();
            if (bundle == null) 
                return;
            
            for (Field field : declaredFields) 
                Autowired annotation = field.getAnnotation(Autowired.class);
                //2. 找到注解后获取他的值
                if (field.isAnnotationPresent(Autowired.class)) 
                    //获得key
                    String key = TextUtils.isEmpty(annotation.value()) ? field.getName() : annotation.value();

                    if (bundle.containsKey(key)) 
                        //获取传输的值
                        Object obj = bundle.get(key);

                        //获得数组单个元素类型 下面需要用到
                        Class<?> componentType = field.getType().getComponentType();
                        //判断如果是Parcelable[] 素组
                        if (field.getType().isArray() && Parcelable.class.isAssignableFrom(componentType)) 
                            //创建对应类型并拷贝
                            Object[] objs = (Object[]) obj;//强转
                            //拷贝数据  (Class<? extends Object[]>) field.getType())   通过getType 获取真正的反射类型
                            Object[] objects = Arrays.copyOf(objs, objs.length, (Class<? extends Object[]>) field.getType());
//                            Object[] objects = Arrays.copyOf(objs, objs.length, Parcelable[].class);
                            obj = objects;//赋值
                        

                        //3. 反射设置属性的值
                        field.setAccessible(true);
//                    Class<?> type = field.getType();
                        field.set(activity, obj);
                    
                
            
         catch (IllegalAccessException e) 
            e.printStackTrace();

        
    
 

ASM插件安装 使用字节码插桩技术时这个插件很有用

以上是关于java筑基.泛型,反射,注解-利用注解加反射练习的主要内容,如果未能解决你的问题,请参考以下文章

《Java架构筑基》从Java基础讲起——泛型与反射

狂神说Java笔记--反射和注解部分笔记

Day642.反射注解和泛型问题 -Java业务开发常见错误

java注解和反射制作dao基类的练习

Java注解教程:自定义注解示例,利用反射进行解析

注解与反射-复习