网站建设公司 南宁,网站推广方法主要有什么,房屋在线设计平台,江苏省建设厅官网文章目录基础说明类型擦除无限定有限定转换泛型表达式方法类型擦除#xff08;桥方法#xff09;关于重载的一些说明总结基础说明
虚拟机没有泛型类型对象一所有对象都属于普通类。在泛型实现的早期版本中#xff0c;甚至能够将使用泛型的程序编译为在1.0虚拟机上运行的类文…
文章目录基础说明类型擦除无限定有限定转换泛型表达式方法类型擦除桥方法关于重载的一些说明总结基础说明
虚拟机没有泛型类型对象一所有对象都属于普通类。在泛型实现的早期版本中甚至能够将使用泛型的程序编译为在1.0虚拟机上运行的类文件 由于泛型是在1.5才引入的为了兼容在java文件编译后是肯定看不见泛型的。也就是类型擦除下面就来介绍一下类型擦除 类型擦除
无论何时定义一个泛型类型都会自动提供一个相应的原始类型raw type)。这个原始 类型的名字就是去掉类型参数后的泛型类型名。类型变量会被擦除(erased),并替换为其限 定类型或者对于无限定的变量则替换为Object)。 无限定
下面先来看下面的代码
public class MyToolT {private T info;public T getInfo() {return info;}public void setInfo(T info) {this.info info;}
}这就是很简单的一个泛型类。现在我通过反射来查看T是什么类型。 public static void main(String[] args) throws NoSuchMethodException {// 得到getInfo方法Method getInfo MyTool.class.getDeclaredMethod(getInfo);System.out.println(getInfo返回值类型为getInfo.getReturnType().getName());}上面运行结果如下 可以发现如果没有指定泛型那么在编译过后T被替换为了Object 即使我们指定了泛型T还是会被替换为Object public static void main(String[] args) throws NoSuchMethodException {MyToolComparable myTool new MyTool();// 得到getInfo方法Method getInfo myTool.getClass().getDeclaredMethod(getInfo);System.out.println(getInfo返回值类型为 getInfo.getReturnType().getName());}有限定
上面的泛型类没有限制下面来看一下有限定的情况
public class ToolT extends Serializable {private T info;public T getInfo() {return info;}public void setInfo(T info) {this.info info;}
}还是使用反射来查看T类型 // 得到getInfo方法Method getInfo Tool.class.getDeclaredMethod(getInfo);System.out.println(getInfo返回值类型为getInfo.getReturnType().getName());运行结果如下 可以发现限定符替换了T。 上面是一个限定符的如果有两个或者多个限定符呢
public class MulToolT extends Comparator Comparable Serializable {public T t;public T getInfo() {return t;}
}还是使用上面的反射代码输出如下 可以发现返回的是Comparator下面来交换一下限定的位置分别让Comparable 和Serializable成为第一个自己交换即可。交换后代码运行结果如下 通过上面的运行结果我们就可以得出结论使用了类型限定符那么第一个限定就会替换T 转换泛型表达式
还是上利用MyTool代码举例
public class MyToolT {private T info;public T getInfo() {return info;}public void setInfo(T info) {this.info info;}
}我们经过上面的学习知道会进行类型擦除上面的MyTool的T会被替换为Object。那么getInfo返回值就是Object的类型但是我们在实际调用getInfo方法时只要传入了类型那么返回值就是我们传入的类型。看下面代码 public static void main(String[] args) {MyToolString stringMyTool new MyTool();stringMyTool.setInfo(xxx);// 得到所有方法Method[] declaredMethods stringMyTool.getClass().getDeclaredMethods();for (Method declaredMethod : declaredMethods) {String methodName declaredMethod.getName(); // 方法名String returnType declaredMethod.getReturnType().getName(); // 返回类型System.out.println(方法名 methodName --返回类型 returnType);}// 返回的类型为StringString info stringMyTool.getInfo();}运行结果如下 可以发现getInfo返回值确实为Object但是我们 String info stringMyTool.getInfo(); 这条语句并没有进行强转这就说明编译器已经帮我们进行了强转。其实在调用stringMyTool.getInfo()编译器将其转换为了2条虚拟机指令
对于MyTool.getInfo()的调用将返回值Object强转为String
上面是对方法返回值进行强转其实对字段的访问也是一样的如果将info字段修饰符改为public也可以直接使用String进行接收 String filed stringMyTool.info;方法类型擦除桥方法
我们不说啥理论直接看下面代码
public class AnimalT {public void setX(T t) {}
}这个一个泛型类有一个set方法
public class Cat extends AnimalString {Overridepublic void setX(String s) {}
}这是Cat类继承了Animal类指定了泛型为String并且重写了setX方法。
下面就是使用Cat AnimalString animal new Cat();animal.setX(hello world!!!);大家看看这个代码有没有发现问题我们使用Animal来接收了一个Cat对象这是正确的。但是animal.serX就不怎么对劲了。下面我来分析一下
由于Animal会发生类型擦除所以animal.setX实际会调用 Animal.setX(Object)由于animal引用的是一个Cat所以会去寻找Cat.setX(Object)问题出现了Cat根本没有setX(Object)只有setX(String)
可以发现类型擦除和多态产生了冲突。为了解决这个问题编译器会在Cat类中生成一个桥方法。在Cat中生成的桥方法如下 public void setX(Object s){setX((String) s);}其实就是生成了一个参数为Object类型的setX方法这个方法会去调用参数为String类型的方法就好像桥梁的作用一样所以我们成为桥方法。
为了验证上面的说法也就是编译器会给我们的代码生成一个桥方法下面我就使用反射输出Cat的所有方法。 public static void main(String[] args) {// 得到所有方法Method[] declaredMethods Cat.class.getDeclaredMethods();for (Method declaredMethod : declaredMethods) {String methodName declaredMethod.getName(); // 方法名// 参数类型集合ListString types Arrays.stream(declaredMethod.getParameterTypes()).map(Class::getTypeName).collect(Collectors.toList());System.out.println(方法名 methodName --参数类型 types);}}上面的代码输出如下 可以发现编译器确实给我们生成了一个setX方法参数类型就是Object这个方法就是一个桥方法。有了这个桥方法多态和类型擦除的问题也就解决了。 关于重载的一些说明
通过上面的例子大家应该对桥方法有了清晰的认识有些思想活跃的人可能就会觉得不太对劲了。大家回想一下重载的定义重载就是参数名相同参数不同。
这确实没问题下面我在Animal定义应该getT方法然后在Cat里面重写这个方法
public class AnimalT {private T t;public void setX(T t) {}public T getT() {return t;}
}public class Cat extends AnimalString {Overridepublic void setX(String s) {}Overridepublic String getT() {return ;}
}根据上面的桥方法大家想一下是不是在Cat里面会生成应该 public Object getT()方法呢我们还是通过的反射代码查看代码和运行结果如下 public static void main(String[] args) {// 得到所有方法Method[] declaredMethods Cat.class.getDeclaredMethods();for (Method declaredMethod : declaredMethods) {String methodName declaredMethod.getName(); // 方法名// 参数类型集合ListString types Arrays.stream(declaredMethod.getParameterTypes()).map(Class::getTypeName).collect(Collectors.toList());// 得到返回类型String returnType declaredMethod.getReturnType().getName();System.out.println(方法名 methodName \t\t参数类型 types \t\t返回类型 returnType);}}可以发现在Cat里面存在了2个同名的方法并且参数相同这已经违法了重载的定义按理说程序应该直接报错但是并没有原因就是在虚拟机中会由参数类型和返回类型共同指定一个方法上面代码中参数为Object的getT方法就是一个桥方法。 总结
在最后对于java泛型的转换我们需要记住以下几点
虚拟机中没有泛型只有普通的类和方法所有的类型参数都会替换为它们的限定类型会通过合成桥方法来保持多态为保持类型安全性必要时会插入强制类型转换
关于泛型的更多知识参考以下内容
泛型程序设计基础 类型擦除、桥方法、泛型代码和虚拟机 泛型的限制及其继承规则 泛型的通配符(extends,super,?)