转载

java遗珠之泛型类型擦除

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lastsweetop/article/details/83025092

擦除规则

泛型的作用之前已经介绍过了只是用于编译之前更为严格的类型检查,其他的一些特性也都是编译之前的,在编译之后泛型是会被擦除掉的。

类型擦除所做的事情如下:

Object

泛型类型的擦除

无边界类型擦除

public class Node<T> {

    private T data;
    private Node<T> next;

    public Node(T data, Node<T> next) {
        this.data = data;
        this.next = next;
    }

    public T getData() { return data; }
    // ...
}

按照规则第一条 T 是无界的,会用 Object 来代替

public class Node {

    private Object data;
    private Node next;

    public Node(Object data, Node next) {
        this.data = data;
        this.next = next;
    }

    public Object getData() { return data; }
    // ...
}

有边界类型擦除

public class Node<T extends Comparable<T>> {

    private T data;
    private Node<T> next;

    public Node(T data, Node<T> next) {
        this.data = data;
        this.next = next;
    }

    public T getData() { return data; }
    // ...
}

有界限的会替换成边界类型

public class Node {

    private Comparable data;
    private Node next;

    public Node(Comparable data, Node next) {
        this.data = data;
        this.next = next;
    }

    public Comparable getData() { return data; }
    // ...
}

泛型方法的类型擦除

无边界类型擦除

public static <T> int count(T[] anArray, T elem) {
    int cnt = 0;
    for (T e : anArray)
        if (e.equals(elem))
            ++cnt;
        return cnt;
}

和泛型类型同理,擦除如下:

public static <T> int count(T[] anArray, T elem) {
    int cnt = 0;
    for (T e : anArray)
        if (e.equals(elem))
            ++cnt;
        return cnt;
}

有边界类型擦除

class Shape { /* ... */ }
class Circle extends Shape { /* ... */ }
class Rectangle extends Shape { /* ... */ }
public class Util {
    public static <T extends Shape> void draw(T shape) { /* ... */ }
}

类型擦除桥接方法

想想下面那个类的擦除

public class ANode<T> {

    public T data;

    public ANode(T data) { this.data = data; }

    public void setData(T data) {
        System.out.println("Node.setData");
        this.data = data;
    }

    public T getData() { return data; }
    // ...
}

会被擦除成

public class ANode {

    private Object data;

    public ANode(Object data) { this.data = data; }

    public void setData(Object data) {
        System.out.println("Node.setData");
        this.data = data;
    }

    public Object getData() { return data; }
    // ...
}

然后再看它的子类

public class MyNode extends ANode<Integer> {
    public MyNode(Integer data) {
        super(data);
    }
    
    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
}

擦除之后

public class MyNode extends ANode {
    public MyNode(Integer data) {
        super(data);
    }
    
    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
}

这就有意思了我们在main方法里调用如下:

public static void main(String[] args) {
        ANode n = new MyNode(5);
        n.setData("Hello");
        Integer x = (Integer) n.getData();    // Causes a ClassCastException to be thrown.
    }

如果只看擦除前的代码, setData("Hello") 调用的应该是子类的方法,参数不应该是String才对。

但是代码肯定是经过擦除的,从这个角度来说

setData("Hello")
getData()

嗯,到目前为止只是我们期望的,那么一运行,蒙了,直接在 setData("Hello") 就报错了。

Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
	at com.sweetop.studycore.generic.MyNode.main(MyNode.java:18)

这是因为为了保证泛型类型擦除后依然具有多态性,编译器在编译的时候自动增加了一个桥接方法,因此子类擦除后应该是这样的

public class MyNode extends ANode {
    public MyNode(Integer data) {
        super(data);
    }
    
    public void setData(Object data) {
        setData((Integer) data);
    }
    
    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
}

如果是这样的话,那么子类就重写了父类的方法,调用的其实还是子类的 setData(Object data) .

因此就看到了上面那个错误。

之后碰到继承泛型类型的时候,一定要注意这种问题。最好在使用之前先做下类型判断

public static void main(String[] args) {
        ANode n = new MyNode(5);
        Object a = "1";
        if (a instanceof Integer) {
            n.setData("1");
        }
        Integer x = (Integer) n.getData();    // Causes a ClassCastException to be thrown.
}
原文  https://blog.csdn.net/lastsweetop/article/details/83025092
正文到此结束
Loading...