编译器如何区分<! - ?扩展Number - > from <number>,当它们在类型擦除后似乎都被编译成相同的东西时?

时间:2016-01-18 02:15:49

标签: java covariance type-erasure

我试图理解java generic covariancy,我理解为什么

<div class="main">

是不允许的,因此不会将浮动放入列表中,从而导致问题。我相信类型擦除会将其编译为:

List<Number> list = new ArrayList<Integer>();
Integer = list.get(0);

这是允许的:

List list = new ArrayList();
Integer = (Integer) list.get(0);

在类型擦除后,这还没有编译成同样的东西吗? 通配符如何帮助编译器强制浮点数在保存整数时不会被放入列表中?

2 个答案:

答案 0 :(得分:5)

如果您有List<? extends Number> list,则无法将任何放入结果列表中。您的列表是&#34;列表,其中包含一些扩展Number&#34;的对象,因此使用任何参数调用add()与此类型不兼容。所以这就是编译器控制你是否放置浮动的方式:它不允许放任何东西。

通常只在您不打算修改列表的地方将类型更改为List<? extends Number>,并且只想阅读它。

答案 1 :(得分:1)

Tagir非常好的回答,我想补充几点。  首先,以下内容甚至不会编译:

List<? extends Number> list = new List<Integer>();

我希望你知道List是一个界面。所以现在我们有:

List<? extends Number> list = new ArrayList<Integer>();

此处listList<? extends Number>类型的引用,它只是意味着它可以引用一个列表,其中每个元素 IS Number或更具体地说每个元素element扩展了类Number,因此它意味着允许以下所有内容:

list = new ArrayList<Float>();
list = new ArrayList<Long>();

但我们无法将Object列为Object,而Number不是list = new ArrayList<Object>(); // compile time error

Number

现在,当我们从这个列表中取出元素时,我们只知道有一件事情确定它是Integer,这意味着不确定但它可以是Long或{{1}也是。所以我们需要铸造:

Integer integer = (Integer) list.get(0);
Float floatValue = (Float) list.get(0);
Number number = list.get(0); // no casting if assigning it to Number

当我们生成元素而不消耗它们时,这非常有用(检查PECS)。例如,在打印元素时很有用,但是当你想在其中添加任何元素时它们没用,因为它甚至都不会编译。

private static void printMe(List<? extends Number> numbers) {
        for(Number number : numbers) {
            System.out.println(number);
        }
}

这可以调用为:

List<Integer> integerList = new ArrayList<>();
integerList.add(10);integerList.add(20);integerList.add(30);
printMe(integerList);

List<Long> longList = new ArrayList<>();
longList.add((long) 4.5);longList.add((long) 6.5);longList.add((long) 7.5);
printMe(longList);

这将只打印每个列表中的元素。