本文转载自微信公众号《是的'练级攻略》,作者是是。转载本文请联系yes的练级指导公众号。大家好,我是。今天我们来谈谈泛型。其实我初学的时候对泛型有点迷茫,因为看到有人说Java的泛型不是真正的泛型,看不懂。有人说Java的泛型在真正运行的时候会抹掉类型。我想擦除是什么意思?为什么要擦除?那为什么擦除类型的时候得到的是泛型呢?类型信息?今天让我们来看看泛型:为什么我们需要泛型?为什么说Java的泛型是伪泛型呢?为什么Java泛型的实现是类型擦除?类型既然被抹掉了,为什么还在运行呢?还能通过反射得到类型吗?话不多说,走吧!为什么我们需要泛型?我们都知道Java5之前是没有泛型的,不用泛型也能很好用,那为什么还要加一个泛型呢?它给我们带来了什么?我们先看下面的代码:Listlist=newArrayList();list.add("yes");//添加stringlist.add(233);//添加intwithoutgenerics时,添加的集合的数据会不受任何约束,将被视为对象类型。可能有人会说,这很好,多自由啊!的确,自由就是自由,但是代码的约束能力越低,越容易出错,使用上也有很多不便,比如获取的时候需要强行切换。如果不小心弄错了类型,编译的时候会通过,但是运行的时候会报错。总之,Java引入了泛型。泛型的作用是加一层约束,对类型进行约束。有了这层约束,事情就好办了。因为声明了类型,所以可以在编译时识别不正确的类型元素。尽早抛出错误以避免在运行时发现。而且不需要在代码上强行显示。从下面的代码可以看出,可以直接获取到String类型的元素。总结一下泛型的好处:提高了代码的可读性,一眼就能看出集合的类型(其他泛型类)可以在编译时检查类型安全,增加了程序的健壮性和不需要强制转换(其实内部帮助已经做了强转换,下面会讲到)提高代码复用率,定义泛型,一个方法(类)可以适配所有类型(其实是Object之前也还好,就是比较麻烦)为什么说Java呢?泛型是伪泛型。看来我们平日用的泛型没什么问题。为什么说Java的泛型是伪泛型呢?伪泛型在哪里?我们再看一段代码:可以看到,我声明的是一个String类型的集合,但是通过反射将int类型的数据插入到集合中,并且成功了???这说明泛型类型在运行时根本不起作用!也就是说,JVM在运行时无法获取到Generic信息,不会受到任何约束。你可以认为Java的泛型在编译时生效,运行时没有泛型,所以大家都说Java是伪泛型!因此,虽然泛型是在IDE写代码的时候生效的,但实际运行时,泛型有时会被抹掉。一句话,Java的泛型只在编译时生效,JVM运行时是没有泛型的。为什么Java泛型的实现是类型擦除?类型擦除(类型擦除)。Java之所以在运行时擦除类型是为了向后兼容,即兼容Java5之前的编译类文件。例如,在Java1.2上运行的代码可以在Java5JRE上运行。正是为了这种该死的向后兼容性,Java实现了伪泛型。我从现有的实现中推导出伪泛型设计的可能思路(个人废话,大家随便听听)是这样的:在Java5之前的版本中,已经有很多应用在线运行,而且我好像不能加新集会影响推广,可能会被骂得很惨。毕竟,泛型添加了一个约束。前面的代码没有这个约束。怎么可能兼容呢?泛型在编译时被识别和约束,然后泛型信息在编译后被擦除。这么跑约束不就没了吗?这不是和以前一样吗?嗯,就是这样!总而言之,类型擦除用于向后兼容。这里还有一个坑,就是泛型不支持基本类型,比如int。因为擦除后泛型变成了Object,这个int兼容Object有点麻烦。网上看了R大学的解释如下:GJ/Java5说:这个问题有点麻烦,在这个版本发布之前无法完成,那就不管了。所以Java5的泛型不支持原始类型,我们还要写恶心的ArrayList,ArrayList……这是偷懒的地方。emmm,这是什么意思?Java的写手也是程序员,也有在线发布的需求,所以……好了,言归正传,现在Java的泛型实现确实是伪泛型。看到这里,有人会问了?有没有可能我们只能一直使用伪泛型?嗯,我想,只要时间允许,只要钱够,应该可以做吧?哈哈哈。既然类型已经被擦除,为什么运行时可以通过反射获取到类型呢?难道是没有擦干净?别着急,我们一起来看看吧。我们先回顾一下这段代码:我们定义了一个泛型为String的列表,获取到的str不需要强制,这一步是怎么做的呢?我们用javap-c看字节码:我们从reverseCompile开始,看生成的字节码,可以看到new的list没有保存generic信息,所以被抹掉了。然后看#7,有checkcast,强制转换的类型是String。大家应该明白为什么type会被擦除,但是我们拿到的时候不需要强行?因为编译器隐式的帮我们插入了强制转发的代码!所以我们不需要在我们的Java代码中写强制转发。回到本节标题:既然擦除了类型,为什么在运行时可以通过反射获取到类型呢?答案隐藏在类文件中。我们看这段代码:通过反射,我确实得到了列表的类型。现在类型已经被擦除,这是怎么做到的呢?我们直接执行第一手的javap-v,反编译,看到字节码中有这么一条记录:这个很好理解,class文件中包含了这个信息,所以我们自然可以通过反射得到这个类型。是的,就这么简单。因为这个原则,我们只能针对以下三种情况使用反射获取泛型类型:泛型成员变量泛型方法输入参数泛型方法返回值对局部变量无能为力。最后,今天关于泛型的文章暂时先到这里。其实我们还没讲完泛型,比如通配符,上下界(泛型PECS原则),泛型桥接和桥接坑。东西还挺多的,文章就放下吧!等待。参考https://www.zhihu.com/question/28665443/answer/118148143
