public class Node { private Object data; private Node next; public Node(Object data, Node next) { this.data = https://www.isolves.com/it/cxkf/yy/JAVA/2019-08-07/data; this.next = next; } public Object getData() { return data; }}这意味着不管我们声明Node还是Node , 到了运行期间 , JVM统统视为Node 。有没有什么办法可以解决这个问题呢?这就需要我们自己重新设置bounds了 , 将上面的代码修改成下面这样:
public class Node<T extends Comparable<T>> { private T data; private Node<T> next; public Node(T data, Node<T> next) { this.data = https://www.isolves.com/it/cxkf/yy/JAVA/2019-08-07/data; this.next = next; } public T getData() { return data; }}这样编译器就会将T出现的地方替换成Comparable而不再是默认的Object了:
public class Node { private Comparable data; private Node next; public Node(Comparable data, Node next) { this.data = https://www.isolves.com/it/cxkf/yy/JAVA/2019-08-07/data; this.next = next; } public Comparable getData() { return data; }}上面的概念或许还是比较好理解 , 但其实泛型擦除带来的问题远远不止这些 , 接下来我们系统地来看一下类型擦除所带来的一些问题 , 有些问题在C++的泛型中可能不会遇见 , 但是在Java中却需要格外小心 。
问题一
在Java中不允许创建泛型数组 , 类似下面这样的做法编译器会报错:
List<Integer>[] arrayOfLists = new List<Integer>[2];为什么编译器不支持上面这样的做法呢?继续使用逆向思维 , 我们站在编译器的角度来考虑这个问题 。
我们先来看一下下面这个例子:
Object[] strings = new String[2];strings[0] = "hi";strings[1] = 100;对于上面这段代码还是很好理解 , 字符串数组不能存放整型元素 , 而且这样的错误往往要等到代码运行的时候才能发现 , 编译器是无法识别的 。接下来我们再来看一下假设Java支持泛型数组的创建会出现什么后果:
Object[] stringLists = new List<String>[];stringLists[0] = new ArrayList<String>();stringLists[1] = new ArrayList<Integer>();假设我们支持泛型数组的创建 , 由于运行时期类型信息已经被擦除 , JVM实际上根本就不知道new ArrayList()和new ArrayList()的区别 。类似这样的错误假如出现才实际的应用场景中 , 将非常难以察觉 。别乱打日志了 , 这才是正确的打日志姿势!
如果你对上面这一点还抱有怀疑的话 , 可以尝试运行下面这段代码:
public class ErasedTypeEquivalence { public static void main(String[] args) { Class c1 = new ArrayList<String>().getClass(); Class c2 = new ArrayList<Integer>().getClass(); System.out.println(c1 == c2); }}问题二
继续复用我们上面的Node的类 , 对于泛型代码 , Java编译器实际上还会偷偷帮我们实现一个Bridge method 。
public class Node<T> { public T data; public Node(T data) { this.data = https://www.isolves.com/it/cxkf/yy/JAVA/2019-08-07/data; } public void setData(T data) { System.out.println("Node.setData"); this.data = data; }}public class MyNode extends Node
public class Node { public Object data; public Node(Object data) { this.data = https://www.isolves.com/it/cxkf/yy/JAVA/2019-08-07/data; } public void setData(Object data) { System.out.println("Node.setData"); this.data = data; }}public class MyNode extends Node { public MyNode(Integer data) { super(data); } public void setData(Integer data) { System.out.println("MyNode.setData"); super.setData(data); }}实际上不是这样的 , 我们先来看一下下面这段代码 , 这段代码运行的时候会抛出ClassCastException异常 , 提示String无法转换成Integer:
MyNode mn = new MyNode(5);Node n = mn;n.setData("Hello");如果按照我们上面生成的代码 , 运行到第3行的时候不应该报错(注意我注释掉了第4行) , 因为MyNode中不存在setData(String data)方法 , 所以只能调用父类Node的setData(Object data)方法 , 既然这样上面的第3行代码不应该报错 , 因为String当然可以转换成Object了 , 那ClassCastException到底是怎么抛出的?
实际上Java编译器对上面代码自动还做了一个处理:
class MyNode extends Node { public void setData(Object data) { setData((Integer) data); } public void setData(Integer data) { System.out.println("MyNode.setData"); super.setData(data); }}
推荐阅读
- 熟普的功效和作用大全
- 茶对应茶效大全
- JavaScript获取json中key所对应的value值的简单方法
- Java进程CPU占用高导致的网页请求超时的故障排查
- 谁说JAVA不适合做神经网络,那来看看Encog这款框架吧
- 圣经书解释最完整的一本书 圣经的解释大全的书
- 保护环境的标语宣传语大全 保护环境的警示语
- 中国茶具大全
- JavaScript中执行上下文和执行栈
- Android实现Rxjava2+Retrofit完美封装
