java泛型详解

来源:互联网 发布:高清网络监控 编辑:程序博客网 时间:2024/06/11 01:15

java泛型是java语言中的一颗语法糖,对java的功能没有什么影响,但是能够方便程序员的使用,减少出错的机会。比如hashMap的get()方法,获取的类型是一个Object,java中的所有类型都继承自java.lang.Object类,那么get()方法获取到的对象就存在向任何类型转换的可能,除了程序员自己和运行期的虚拟机,没人知道get到的这个对象到底是什么类型,也就容易出错。还是举一个例子吧:

package test;import java.util.ArrayList;import java.util.List;public class GenericityTest {    public static void main(String[] args) {        List list = new ArrayList();        list.add(1);        list.add(new Dog());        for(Object m : list){            System.out.println((Dog)m);        }    }}class Dog{    @Override    public String toString() {        return String.valueOf(this.hashCode());    }}

这段代码在编译期间是没有问题的,因为迭代器返回的对象在编译期是不知道它到底是什么类型,我在list中放了一个int类型的1,又放了一只狗,结果会在运行期间报类型转换错误。假如写成List《Dog》。。。。,加上泛型就能够在编译期间发现这个错误,大家可以自己试一下。
java中的泛型所起的作用就是一个参数的作用,这个参数规定了在集合里能放什么类型,放了不符合参数规定的类型就会在编译期报错。
接下来稍微深入一下,泛型在运行期间到底起到作用了没,答案是否定的,java的泛型实现是用的类型擦除的技术,在编译成class之后,泛型也就消失了,
在java中List《String》和List《Int》类型是同一个类型,也就是说java是一个伪泛型,了解C#的可能知道在C#中List《String》和List《Int》是两个不同的类型。为了证明举了下面这个例子:

package test;import java.util.ArrayList;import java.util.List;public class GenericityTest {        public static void say(List<String> list){//编译器报错        System.out.println("String");    }    public static void say(List<Integer> list){//编译器报错        System.out.println("Integer");    }    public static void main(String[] args) { }}

为什么会这样,因为java编译成class后会擦除掉泛型,所以两个say()方法在class中完全一样从而报错。但是加入非要通过泛型来重载的话,就必须采取一个并不好的方案,改变返回值。有人会说了,java语言中什么时候用返回值来重载了啊,你瞎说!表面上是这样,其实并不是根据返回值来重载,而是通过返回值的不同来防止编译期间的报错,虽然java语言的重载不包括返回值的不同来区分不同的方法,但是在class文件中方法的描述符除了方法名,参数个数类型外,还包括方法的返回值,只有返回值不同的两个方法在class中是可以区分和共存的。不明白的可以结合下面这个例子理解:

package test;import java.util.ArrayList;import java.util.List;//必须使用jdk 1.6的javac才能够编译成功,别的编译器或者jdk版本仍然拒绝编译public class GenericityTest {    public static  int say(List<String> list){//返回int        System.out.println("String");        return 0;    }    public static  String say(List<Integer> list){//返回String        System.out.println("Integer");        return "ss";    }    public static void main(String[] args) {        say(new ArrayList<String>());        say(new ArrayList<Integer>()); }}

对于类型擦除,有兴趣的可以反编译试试,写一个List《String》……,反编译后发现泛型都不见了,变成了List……。
最后补充一点,类型擦除并不意味着完全没有泛型的信息,而是对方法区的字节码进行擦除,在想list中存对象的时候是当成Object存的, LocalVariableTypeTable中的signature中还保留这泛型的信息。
这里写图片描述

0 0
原创粉丝点击