泛型

原创
2017/11/21 09:37
阅读数 155

介绍

泛型是 Java SE 1.5 的新特性,泛型的本质是参数化类型,这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。

在 Java SE 1.5 之前没有泛型的情况的下只能通过对类型 Object 的引用来实现参数的任意化。其带来的缺点是要做显式强制类型转换,而这种强制转换编译期是不做检查的,容易把问题留到运行时,所以 泛型的好处是在编译时检查类型安全,并且所有的强制转换都是自动和隐式的,提高了代码的重用率,避免在运行时出现 ClassCastException。

JDK 1.5 引入了泛型来允许强类型在编译时进行类型检查;JDK 1.7 泛型实例化类型具备了自动推断能力,譬如

List<String> list = new ArrayList<String>();

可以写成

List<String> llist = new ArrayList<>();

了,JDK 具备自动推断能力。

原理

泛型是通过类型擦除来实现的,编译器在编译时擦除了所有泛型类型相关的信息,所以在运行时不存在任何泛型类型相关的信息,譬如 List<Integer> 在运行时仅用一个 List 来表示,这样做的目的是为了和 Java 1.5 之前版本进行兼容。

泛型擦除具体来说就是在编译成字节码时首先进行类型检查,接着进行类型擦除(即所有类型参数都用他们的限定类型替换,包括类、变量和方法),接着如果类型擦除和多态性发生冲突时就在子类中生成桥方法解决,接着如果调用泛型方法的返回类型被擦除则在调用该方法时插入强制类型转换

泛型类、泛型接口、泛型方法

泛型类是在实例化类的对象时才能确定的类型,其定义譬如 class Test<T> {},在实例化该类时必须指明泛型 T 的具体类型。

泛型接口与泛型类一样,其定义譬如 interface Generator<E> { E dunc(E e); }。

泛型方法所在的类可以是泛型类也可以是非泛型类,是否拥有泛型方法与所在的类无关,所以在我们应用中应该尽可能使用泛型方法,不要放大作用空间,尤其是在 static 方法时 static 方法无法访问泛型类的类型参数,所以更应该使用泛型的 static 方法(声明泛型一定要写在 static 后返回值类型前)。泛型方法的定义譬如 <T> void func(T val) {}。

擦除

    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException,
            IllegalAccessException {
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.getClass().getMethod("add",Object.class).invoke(list,2);

        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }

实例化泛型对象或者泛型数组可通过反射实现,如下

public class ArrayWithTypeToken<T> {  

   private T[] array;



   public ArrayWithTypeToken(Class<T> type, int size) {  

      array = (T[]) Array.newInstance(type, size);  

   }



   public void put(int index, T item) {  

       array[index] = item;  

   }



   public T get(int index) {  

       return array[index];  

   }



   public T[] create() {  

       return array;  

   }

}

ArrayWithTypeToken<Integer> arrayToken = new ArrayWithTypeToken<Integer>(Integer.class, 100);

Integer[] array = arrayToken.create();

static <T> T newTclass(Class<T> clazz) throws

                       InstantiationException,

                       IllegalAccessException {  

   T obj = clazz.newInstance();  

   return obj;  

}

泛型擦除其实是分情况擦除的,不是完全擦除:

Java 在编译时会在字节码里指令集之外的地方保留部分泛型信息,泛型接口、类、方法定义上的所有泛型、成员变量声明处的泛型都会被保留类型信息,其他地方的泛型信息都会被擦除。

感兴趣的可以自己编写各种场景的泛型代码然后编译后反编译查看即可发现。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部