首页 > 技术文章 > 泛型通配符以及注解

xiaofengshan 2021-05-16 20:24 原文

泛型定义:

使用泛型的目的是利用Java编译机制,在编译过程中帮我们检测代码中不规范的有可能导致程序错误的代码。例如,我们都知道List容器可以持有任何类型的数据,所以我们可以把String和Integer等类型同时放入同一个List容器中,但是这种做法是极其危险的。在泛型机制中,这种操作就会导致编译不通过,会强制要求你将List容器中的数据类型修改为统一类型。这种机制可以帮助我们减少程序运行中隐藏的Bug。

作用

用来规范类型

用法:

  • T,E,K,V,?

本质上这些个都是通配符,没啥区别,只不过是编码时的一种约定俗成的东西。比如上述代码中的 T ,我们可以换成 A-Z 之间的任何一个 字母都可以,并不会影响程序的正常运行,但是如果换成其他的字母代替 T ,在可读性上可能会弱一些。通常情况下,T,* E,K,V,?是这样约定的:

  • ?表示不确定的 java 类型

  • T (type) 表示具体的一个java类型

  • K V (key value) 分别代表java键值中的Key Value

  • E (element) 代表Element

泛型通配符定义:

泛型和泛型通配符最根本的区别就是Java编译器会把泛型【T】推断成具体类型,而把通配符【?】推断成未知类型。Java编辑器只能操作具体类型,不能操作未知类型,如果有对参数做修改的操作就必须要使用泛型,如果仅仅是查看就可以使用通配符。

泛型通配符代表的是一种未知的类型,常用在方法上

用法:

 @Test
   public   void   test() {

       List<Integer> list1=new ArrayList<>();

       List<String> list2=new ArrayList<>();
       list2.add("123");

       List<?> list3=new ArrayList<>();
       //编译通过
       list3=list1;
       //编译通过
       list3=list2;
       //编译失败
       list3.add("123");
       //获取成功
       System.out.println("结果为----"+ list3.get(0));

      //下界通配
       List<? super Animal> list4=new ArrayList<>();
       //编译通过
       list4.add(new Animal());
       //编译通过
       list4.add(new Person());
       //编译失败
       list4.add(new Object())
               
               
       //上界通配
       List<? extends Person> list5=new ArrayList<>();
       //编译失败
       list5.add(new Person());
   }

?和 T 的区别

  1. 通过 T 来确保泛型参数的一致性,?属于无界通配符,不能保证参数的一致性。
  2. 通配符可以使用超类限定而类型参数不行

类型参数 T 只具有 一种 类型限定方式:
T extends A
但是通配符 ? 可以进行 两种限定:
? extends A
? super A

  1. Class在实例化的时候,T 要替换成具体类。Class<?>它是个通配泛型,? 可以代表任何类型,不需要替换成具体类
// 可以
public Class<?> clazz;
// 不可以,因为 T 需要指定类型
public Class<T> clazzT;
所以当不知道定声明什么类型的 

Class<?>: 无界通配符不能替换成具体类型,上界< ? extends E>和下界< ? super E>可以替换成具体类型

注解定义:

也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。

  • 作用分类:
    ①编写文档:通过代码里标识的注解生成文档【生成文档doc文档】
    ②代码分析:通过代码里标识的注解对代码进行分析【使用反射】
    ③编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】

  • JDK中预定义的一些注解
    * @Override :检测被该注解标注的方法是否是继承自父类(接口)的
    * @Deprecated:该注解标注的内容,表示已过时
    * @SuppressWarnings:压制警告 一般传递参数all @SuppressWarnings("all")

  • 自定义注解
    * 格式:
    元注解
    public @interface 注解名称{
    属性列表;
    }

    • 本质:注解本质上就是一个接口,该接口默认继承Annotation接口

      • public interface MyAnno extends java.lang.annotation.Annotation {}
    • 属性:接口中的抽象方法

      • 要求:
      1. 属性的返回值类型有下列取值
        * 基本数据类型
        * String
        * 枚举
        * 注解
        * 以上类型的数组

      2. 定义了属性,在使用时需要给属性赋值

        1. 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
        2. 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。
        3. 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略
示例
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AnnotationDemo {

    String value() default "";

    int age() default 15;

}

元注解:用于描述注解的注解

  • @Target:描述注解能够作用的位置

  • ElementType取值:

  • TYPE:可以作用于类上

  • METHOD:可以作用于方法上

  • FIELD:可以作用于成员变量上

  • @Retention:描述注解被保留的阶段

    • @Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到
  • @Documented:描述注解是否被抽取到api文档中

  • @Inherited:描述注解是否被子类继承

  • 在程序使用(解析)注解:获取注解中定义的属性值

    1. 获取注解定义的位置的对象 (Class,Method,Field)
    2. 获取指定的注解
    • getAnnotation(Class)

    其实就是在内存中生成了一个该注解接口的子类实现对象

Gitee地址

泛型: https://gitee.com/zhuayng/foundation-study/tree/develop/JavaBasis/Other/src/main/java/com/yxkj/other/modular/reflection
注解: https://gitee.com/zhuayng/foundation-study/tree/develop/JavaBasis/Other/src/main/java/com/yxkj/other/modular/annotation

参考

泛型: https://www.cnblogs.com/minikobe/p/11547220.html

推荐阅读