乐趣区

关于java:基础篇深入解析JAVA泛型

1 JAVA 的 Type 类型体系

  • 先理解下 java 的 Type 类型体系(类的类 => 类型),Type 是所有类型(原生类型 -Class、参数化类型 -Parameterizedtype、数组类型 -GenericArrayType、类型变量 -TypeVariable、根本类型 -Class)的独特接口; 前两篇反射和注解讲到的 Class<T> 就是 Type 的一实现类

  • Type 上面又有四个子接口类 ParameterizedType、TypeVariable、GenericArrayType、WildcardType

    • List<E> 示意泛型,E 是 TypeVariable 类型,List<String> 则是 ParameterizedType(参数化类型),List<String> 里的 String 称为理论参数类型
    • 具体化泛型中的类型时,能够应用 ? extends 或 ? super 来示意继承关系;如List<? extends Data>,而外面的 ? 称为通配符类型 WildcardType
    • GenericArrayType 示意一种元素类型是 ParameterizedType(参数化类型)或者 TypeVariable(类型变量)的数组类型,如 T[] 或者 List<E>[]
  • 注解是 JDK1.5 才呈现了的,为了示意被注解的类型的,退出 AnnotatedElement 类型,字面意思就是被注解的元素。JDK1.8 又有了 AnnotatedType 将 Type 和被注解元素的概念关联起来。

  • AnnotatedType 也有四个子接口,和 Type 的四个子接口一一对应,如:ParameterizedType 类型被注解则被编译器解析成 AnnotatedParameterizedType: @AnTest("list")List<String>list

2 泛型的概念

  • Java 泛型(generics)是 JDK1.5 中引入的一个新个性,其本质是参数化类型,解决不确定具体对象类型的问题; 其所操作的数据类型被指定为一个参数(type parameter)这种参数类型能够用在类、接口和办法的创立中,别离称为泛型类、泛型接口、泛型办法

泛型: 把类型明确的工作推延到创建对象或调用办法的时候才去明确的非凡的类型

3 泛型类和泛型办法的示例

  • 泛型类的定义
public class MainTest<T> {private  T param;}
public static void main(String[] args){MainTest<String> data = new MainTest<String>(){};
        ParameterizedType genType1 = (ParameterizedType)data.getClass().getGenericSuperclass();
  }
  • 泛型办法的定义
public class MainTest{public static void main(String[] args){printData("siting");
    }
    static  <T> T printData(T t){System.out.println(t);
        return t;
    }
}
  • 接口和抽象类都能够应用泛型

4 类型擦除

  • 创立泛型的实例时,jvm 是会把具体类型擦除的;编译生成的字节码中不蕴含泛型中的类型参数,即 ArrayList<String> 和 ArrayList<Integer> 都擦除成了 ArrayList,也就是被擦除成 ” 原生类型 ”,这就是泛型擦除
public class MainTest {public static void main(String[] args){List<String> strArr  = new ArrayList<>();
        List<Integer> intArr  = new ArrayList<>();
        Type strClazz = strArr.getClass();
        Type intClazz = intArr.getClass();}
}

  • 查看编译后的字节码文件是如何示意的: idea 菜单 -> view -> show ByteCode
public class MainTest<T> {
    T param;
    public static void main(String[] args){MainTest<String> test = new MainTest<>();
        test.setParam("siting");
    }
    public T getParam() {  return param;}
    public void setParam(T param) {this.param = param;}
}
public class com/MainTest {
  ... 省略
  public static main([Ljava/lang/String;)V
   L0
    LINENUMBER 7 L0
    NEW com/MainTest
    DUP
    INVOKESPECIAL com/MainTest.<init> ()V
    ASTORE 1
   L1
    LINENUMBER 8 L1
    ALOAD 1
    LDC "siting"     // 调用类型擦除后的 setParam(Object)
    INVOKEVIRTUAL com/MainTest.setParam (Ljava/lang/Object;)V
   L2
   ... 省略 //getParam 的返回值是 Object
  public getParam()Ljava/lang/Object;
   L0
    LINENUMBER 10 L0
    ALOAD 0
    GETFIELD com/MainTest.param : Ljava/lang/Object;
    ARETURN
   ... 省略 //setParam 的入参是 Object
  public setParam(Ljava/lang/Object;)V
   L0
    LINENUMBER 11 L0
    ALOAD 0
    ALOAD 1
    PUTFIELD com/MainTest.param : Ljava/lang/Object;
    RETURN
   ...
}
  • 能够看出 T(String)都被转换为 Object 类型,最后的初始化的 String 不见了

5 泛型的继承

  • 子类能够指定父类的泛型参数,能够是已知类(Integer、String 等),也能够用子类本人的泛型参数指定
  • 泛型被继承时,且指定父类泛型参数,则额定生成的 ParameterizedType 类型作为子类的父类;如果没有指定父类泛型参数,则间接继承原生类型
public class MainTest<T> {
    T param;
    static public class SubTest1 extends MainTest<String>{}
    static public class SubTest2<R> extends MainTest<R>{}
    //SubTest3 继承的时原生类型
    static public class SubTest3 extends MainTest{}}

6 泛型变量 TypeVariable

  • (先长期定义一个名称,Test<E> 里的 E 为泛型参数);泛型变量 TypeVariable:泛型的泛型参数就是 TypeVariable;当父类应用子类的泛型参数指定本身的泛型参数时;或者泛型属性定义在泛型类 A <T> 中,并应用泛型类 A <T> 的泛型参数 T 时,其泛型参数都会被编译器定为泛型变量 TypeVariable,而不是被擦除
public class MainTest<T> {
    List<T> param;
    public static void main(String[] args) throws Exception{
        Class clazz =  MainTest.class;
        TypeVariable[] typeVariable = clazz.getTypeParameters();
        // 1
        Field field = clazz.getDeclaredField("param");
        ParameterizedType arrayType = (ParameterizedType)field.getGenericType();
        // interface List<E> 的泛型类型 E 被 T, 具体化,因而其被辨认为 TypeVariable
        TypeVariable variable1 = (TypeVariable)arrayType.getActualTypeArguments()[0];
        // 2
        ParameterizedType type = (ParameterizedType)SubTest.class.getGenericSuperclass();
        TypeVariable variable2 = (TypeVariable)type.getActualTypeArguments()[0];
    }
    static class SubTest<R> extends MainTest<R>{}}

7 参数化类型 ParameterizedType

public interface ParameterizedType extends Type {
    // 获取理论参数,List<String> 里的 String; 如果是 List<T> 则是 TypeVariable 类型
    Type[] getActualTypeArguments(); 
    // 获取原始类型 List<String> -> List<E>
    Type getRawType();  
    Type getOwnerType();}
  • 须要留神的点,咱们不能间接获取指定具体参数的泛型的类型,如 Class clazz = List<String>.class 编译时不通过的;还有就是间接通过泛型类 new 创立的对象,其 Class 并非 ParameterizedType 类型,而是泛型自身的 class,示例如下
public class MainTest<T> {public static void main(String[] args){MainTest<String> str = new MainTest<String>();
        Class variable = str.getClass();
        Type genType1 = variable.getGenericSuperclass();}
}

  • 被具体参数化的泛型能力被编译器辨认为 ParameterizedType 类型,有三种形式获取 ParameterizedType 类型
// 1 子类继承泛型时,指定具体参数(能够是 String 等已知类型, 也能够是子类的泛型参数)// 2 获取在类外部定义的泛型属性,需指定具体泛型参数
// 3 部分代码,能够通过泛型的匿名外部子类 (需指定具体泛型参数) 获取 ParameterizedType 类型
public class MainTest<T> {
    List<T> list;
    public static void main(String[] args) throws NoSuchFieldException {SubTest<String> str = new SubTest<>();
        // 形式一
        Class variable = str.getClass();
        // 父类是(521)ParameterizedType 类型
        ParameterizedType genType = (ParameterizedType)variable.getGenericSuperclass();
        // (521)ParameterizedType 类型的原生类型是(479)class com.MainTest
        Type clazz = genType.getRawType();
        //MainTest.class 的原生类型是(479)class com.MainTest
        Class rawClazz = MainTest.class;

        // 形式二,泛型属性
        Field field = rawClazz.getDeclaredField("list");
        // 属性 list 类型是(546)ParameterizedType 类型 List<T>
        ParameterizedType fieldType = (ParameterizedType)field.getGenericType();

        // 形式三
        MainTest<String> sub3 = new MainTest<String>(){};
        // clazz3 是匿名子类
        Class clazz3 =  sub3.getClass();
        // 父类是(555)ParameterizedType 类型
        ParameterizedType genType3 = (ParameterizedType) clazz3.getGenericSuperclass();
        // (555)ParameterizedType 类型的原生类型是(479)class com.MainTest
        Type type3 = genType3.getRawType();}
    public static class SubTest<R> extends MainTest<R>{}}

8 通配符(WildcardType)

无边界通配符:无界通配符 ? 能够适配任何援用类型:

  • 当办法参数须要传入一个泛型时,而且无奈确定其类型时。间接应用无具体泛型变量的泛型,容易造成安全隐患;若在办法代码里进行类型转换,极容易呈现 ClassCastException 谬误
  • 那泛型变量用 Object 代替不就行了?然而泛型类 + 具体参数转变的 ParameterizedType(参数化类型)是不存在继承关系;即 Object 是 String 的父类,然而 List<Object> 和 List<String> 的类型是不同的两个 ParameterizedType,不存在继承关系。于是有了类型通配符?
public static void print(List list){} 
----->>>
public static void print(List<?> list){} 

  • 无界通配符能够匹配任意类型;然而在应用?时,不能给泛型类的变量设置值,因为咱们不晓得具体类型是什么;如果强行设置新值,前面的读容易呈现 ClassCastException 谬误。因而编译器限度了 通配符?的泛型只能读不能写

上界线定通配符 < ? extends E>

  • 想接管一个 List 汇合,它只能操作数字类型的元素【Float、Integer、Double、Byte 等数字类型都行】,怎么做?能够应用List<? extends Number 的子类 >,表明 List 里的元素都是 Number 的子类
    public static void print(List<? extends Number> list) {Number n = new Double("1.0");
        list.add(n);
        Number tmp = list.get(0);
    }

  • 图片里能够看出,存在上界通配符,因为具体类型不确定,也是只能读不能写的

下界限定通配符 < ? super E>

class Parent{ }
class Child extends Parent{ }
public class MainTest<T> {
    T param;
    public static void main(String[] args){MainTest<? super Child> parent_m = new MainTest<>();
        parent_m.setParam(new Child());
        Object parent = parent_m.getParam();}
    public T getParam() {  return param;}
    public void setParam(T param) {this.param = param;}
}

  • 如果定义了通配符是谁的父类,则是下界限定通配符;此类通配符可读可写,转成任意父类都不会呈现 ClassCastException 谬误。
  • 集体猜测:难道是因为 通配符 上界线定通配符 的泛型 向下转型容易呈现 ClassCastException 谬误,而 下界限定通配符 向上转型不会呈现 ClassCastException 谬误,因而 java 标准限度前者编译出错,而前面编译通过?

9 泛型数组(GenericArrayType)

public interface GenericArrayType extends Type {// 取得这个数组元素类型,即取得:A<T>(A<T>[])或  T(T[])Type getGenericComponentType();}
  • GenericArrayType,泛型数组,形容的是 ParameterizedType 类型以及 TypeVariable 类型数组,即形如:Test<T>[][]、T[]等,是 GenericArrayType 的子接口
public class MainTest<T> {T[] param;
    public static void main(String[] args) throws Exception{
        Class clazz =  MainTest.class;
        Field field = clazz.getDeclaredField("param");
        GenericArrayType arrayType = (GenericArrayType)field.getGenericType();
        TypeVariable variable = (TypeVariable) arrayType.getGenericComponentType();}
}


欢送指注释中谬误

关注公众号,一起交换

  • 泛型就这么简略
  • Java 程序员必备根底:泛型解析
  • Java 获取泛型的类型实例详解
退出移动版