java 泛型 擦除_Java泛型和类型擦除
一 前言:初识泛型
废话不说,先来看一段代码:
public class Holder {
private Object data;
public Holder(Object data ){
this.data = data;
}
public void setData(Object data) {
this.data = data;
}
public Object getData() {
return data;
}
public static void main(String[] args){
Holder holder = new Holder(new SomeNode());
SomeNode someNode = holder.getData();
}
}
class SomeNode{}
Holder类是一个容器,它的作用是用来保存其他类的,这里我们用它来保存SomeNode类,随后把它取出来,编译运行,结果如下:
Error:(21, 43) java: incompatible types
required: SomeNode
found: java.lang.Object
意思是,需要的是SomeNode,取出来的却是Object,如此看来,如果我想保存SomeNode类,就只能把data声明为SomeNode:
private SomeNode data;
这就意味着我们需要为每一个类创造一个Holder,这肯定是不行的,于是泛型的作用来了,泛型,可以理解为任何类型,意思是我可以声明一个可以容纳任何类型的容器:
public class Holder {
private T data;
public Holder(T data ){
this.data = data;
}
public void setData(T data) {
this.data = data;
}
public T getData() {
return data;
}
public static void main(String[] args){
Holder holder = new Holder(new SomeNode());
SomeNode someNode = holder.getData();
}
}
class SomeNode{}
注意写法,在类声明后面加个就行了,你也可以加,只是一个占位符,形参而已。然后我们再把它取出来:
Process finished with exit code 0
程序没有报错,如果这时候我们使用holder的set()方法去插入设置一些非SomeNode类型的值,代码如下:
public static void main(String[] args){
Holder holder = new Holder(new SomeNode());
SomeNode someNode = holder.getData();
holder.setData("AAAA");
}
看结果:
Error:(22, 15) java: method setData in class Holder cannot be applied to given types;
required: SomeNode
found: java.lang.String
reason: actual argument java.lang.String cannot be converted to SomeNode by method invocation conversion
泛型机制就自动为我们报错,很方便。
二 泛型类:元组(Tuple),返回多个对象
熟悉python的同学都知道元组的概念,它是一个只读列表,在返回多个结果时是很有用的,我们利用泛型特性来创造一个包含两个对象的元组:
public class Tuple {
public static void main(String[] args){
TwoTuple t = new TwoTuple("Monkey",12);
System.out.println(t.toString());
}
}
class TwoTuple{
final A first;
final B second;
public TwoTuple(A a,B b){
first = a;
second = b;
}
public String toString(){
return "("+first+","+second+")";
}
}
来看结果:
(Monkey,12)
是不是很方便:)如果想要一个长度为3的元组可以这么写:
public class Tuple {
public static void main(String[] args){
ThreeTuple t = new ThreeTuple("Dog",12,true);
System.out.println(t.toString());
}
}
class TwoTuple{
final A first;
final B second;
public TwoTuple(A a,B b){
first = a;
second = b;
}
public String toString(){
return "("+first+","+second+")";
}
}
class ThreeTuple extends TwoTuple{
final C three;
public ThreeTuple(A a,B b,C c){
super(a,b);
three = c;
}
public String toString(){
return "("+first+","+second+","+three+")";
}
}
结果如下:
(Dog,12,true)
三 泛型接口
泛型接口的定义和泛型类的定义类似,我们来定义一个生成器接口:
public interface Generator {
T next();
}
接着我们实现这个接口,来生成斐波拉契数:
public class Fib implements Generator {
private int count = 0;
@Override
public Integer next() {
return fib(count++);
}
private int fib(int n){
if (n<2)
return 1;
else
return fib(n-2) + fib(n-1);
}
public static void main(String[] args){
Fib f = new Fib();
for (int i=0;i<100;i++){
System.out.println(f.next());
}
}
}
四 泛型方法
比起泛型类,我们更推荐去使用泛型方法,泛型方法定义起来也很简单,我们只需将泛型参数放在返回类型前面即可:
public class GenericMethods {
public void f(T x){
System.out.println(x.getClass().getName());
}
public static void main(String[] args){
GenericMethods g = new GenericMethods();
g.f("Hello");
g.f(100);
g.f(true);
}
}
这里我们定义了一个泛型方法f(),并使用getClass获取类的相关信息(关于Class对象的知识点这里),来看结果:
java.lang.String
java.lang.Integer
java.lang.Boolean
这里还要注意一下Varargs(变长参数)机制和泛型的结合:
public class GenericVarargs {
public static List makeList(T...args){
List list = new ArrayList();
for (T item : args){
list.add(item);
}
return list;
}
public static void main(String[] args){
List list = makeList("A","B","C","D");
System.out.println(list);
}
}
结果如下:
[A, B, C, D]
六 类型擦除
在认识类型擦除之前,我们首先要明白编译器对泛型的处理有两种方式:
1.Code specialization
在实例化一个泛型类或者泛型方法是都生成一份新的字节码,比如对于List,List,List产生三份不同的字节码。
2.Code sharing
对每个泛型类只生成唯一的一份目标代码;该泛型类的所有实例都映射到这份目标代码上,在需要的时候执行类型检查和类型转换。参考文章
C++的模板是典型的Code specialization实现,而Java泛型则是Code sharing实现,将多种泛型类形实例映射到唯一的字节码表示是通过类型擦除(type erasue)实现的。对擦除更通俗的理解就是:编译器生成的bytecode是不包涵泛型信息的。我们看下面的代码:
public class ErasedType {
public static void main(String[] args){
Class c1 = new ArrayList().getClass();
Class c2 = new ArrayList().getClass();
System.out.println(c1 == c2);
}
}
结果如下:
true
也就是说我们在实例化ArrayList和实例化ArrayList时是共享一份目标代码的,泛型类类型信息在编译的过程中被擦除了。对于JVM来说,它只看到一份ArrayList(原始类型)而已。我们还可以从反射的角度来理解类型擦除:
public class ErasedType {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
List list = new ArrayList();
list.add("ABC");
list.getClass().getMethod("add",Object.class).invoke(list,123);
System.out.println(list);
}
}
看结果:
[ABC, 123]
我们很顺利的把Integer型的123插入到了String的List里:)
七 后记
由于类型擦除的存在,我们往往会在使用泛型特性的时候遇到一些诡异的问题,由于篇幅原因,这里不展开了:)我将在另外一篇文章中集中的总结一下这方面的问题。
我的微信号是aristark,欢迎交流指正!
java 泛型 擦除_Java泛型和类型擦除相关推荐
- java 类型擦除_java中的类型擦除type erasure
简介 泛型是java从JDK 5开始引入的新特性,泛型的引入可以让我们在代码编译的时候就强制检查传入的类型,从而提升了程序的健壮度. 泛型可以用在类和接口上,在集合类中非常常见.本文将会讲解泛型导致的 ...
- java什么是类型擦除_Java 泛型,你了解类型擦除吗?
泛型,一个孤独的守门者. 大家可能会有疑问,我为什么叫做泛型是一个守门者.这其实是我个人的看法而已,我的意思是说泛型没有其看起来那么深不可测,它并不神秘与神奇.泛型是 Java 中一个很小巧的概念,但 ...
- java泛型(二)、泛型的内部原理:类型擦除以及类型擦除带来的问题
原 java泛型(二).泛型的内部原理:类型擦除以及类型擦除带来的问题 2012年08月29日 23:44:10 Kilnn 阅读数:56717 版权声明:本文为博主原创文章,未经博主允许不得转载. ...
- 3.1_19 JavaSE入门 P18 【泛型】各类泛型对象、通配符、类型擦除
相关链接 Excel目录 目录 P18 [泛型]各类泛型对象.通配符.类型擦除 1 什么是泛型 2 泛型类.接口库 2.1 泛型类定义语法 2.2 常用泛型标识 2.3 使用语法 2.4 泛型类注意事 ...
- java泛型方法 通配符_Java泛型教程–示例类,接口,方法,通配符等
java泛型方法 通配符 泛型是Java编程的核心功能之一,它是Java 5中引入的.如果您使用的是Java Collections ,并且版本5或更高版本,则可以肯定使用了它. 将泛型与集合类一起使 ...
- java泛型面试_Java泛型面试问题
java泛型面试 Java面试中的通用面试问题在相当长的时间内在Java 5周围越来越普遍,许多应用程序都转移到Java 5上,并且几乎所有新的Java开发都发生在Tiger(Java 5的代号)上. ...
- java 泛型转换_Java泛型—类型转换
如下代码编译无法通过: classA{}class B extendsA {}public static void funC(ListlistA) {//... }public static void ...
- java泛型优点_java泛型的作用和好处
转载于:https://blog.csdn.net/u012760183/article/details/52092692 之前参加面试的时候遇到面试官问泛型的作用,只是说了大概自己的理解, 在此转载 ...
- 一句话,讲清楚java泛型的本质(非类型擦除)
?欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. 背景 昨天,在逛论坛时遇到个这么个问题,上代码: public class GenericT ...
最新文章
- NLLLoss CrossEntropyLoss Pytorch
- wxWidgets:wxStyledTextCtrl类用法
- html section 布局,HTML:section标签
- 【CF#505B】Mr. Kitayuta's Colorful Graph (并查集或Floyd或BFS)
- 计算机的我发展趋势,胡生:简述计算机的发展趋势是什么?
- 跟我学SpringMVC目录汇总贴、PDF下载、源码下载
- linux vi调至文件尾,linux vi从当前行复制到文件尾的命令?
- Python format()函数
- ios 合并图片显示
- MacBook Air开启CPU虚拟化支持(Windows10)
- Braintree-国外支付对接(二)
- 又到年底冲刺时,华为小米竞相降价促销
- pycharm如何打开历史_如何在pycharm中反转控制台历史顺序以进行复制粘贴?
- 怎么从安卓设备转移数据到苹果_如何将数据从安卓手机转移到苹果手机
- axure RP文件如何找回_AXURE教程:管理后台页面框架
- [JAVA]递归实现客户端与服务端之间的文件与文件夹传输
- 看完电视剧“天道“的第一次感想记录
- 百度云网盘在线播放视频速度加快的方法
- Windows磁盘管理工具Diskpart之二 管理动态磁盘
- 解决Cleartext HTTP traffic to 01.minipic.eastday.com not permitted
热门文章
- 其实,大部分人都不需要你去培养
- 程序员上班都在做什么?
- 第六节:教你如何在html中绑定数据
- 管家婆打印自定义编辑_打印相关,人手一份!
- 苹果网页归档转html,常用JS转换HTML转义符
- mysql异常关闭7034,SQL ISNULL()、NVL()、IFNULL() 和 COALESCE() 函数
- opengl实现经纹理映射的旋转立方体_《图形编程技术学习》(五十三)环境映射...
- 监督学习 | 非线性回归 之多项式回归原理及Sklearn实现
- R中6种读入表格数据的方式哪个最快?结果出人意料!
- 适用于ps的Raw格式图像插件:AdobeCamera Raw13 Mac中文版