Java 中缩小引用转换规则的反例

问题描述 投票:0回答:1

考虑 JLS 第 1.5.6.1 节中提到的允许缩小引用转换的规则

以下是缩小引用转换的示例:

ArrayList<? extends Integer> l = getList(); // Assume getList() is defined
ArrayList<Number> ll = (ArrayList<Number>) l;

上面的代码被

javac
eclipse JDT
拒绝。我的问题是,据我所知,它满足 JLS 中列出的所有必要约束,以便成为允许的缩小引用转换。所以,我想知道这是否是编译器中的错误,
JLS
或者我误解了某些东西。

以下是应用于该示例的每条规则的演练。首先,我们有源类型

S := ArrayList<? extends Integer>
和目标类型
T := ArrayList<Number>
。为了允许缩小引用转换,JLS 列出了三个约束,为了方便起见,我将交换第二个和第三个:

1。 S 不是 T 的子类型 (§4.10) -> 显然

ArrayList<? extends Integer>
不是
List<Number>

的子类型

3.

S
是类或接口类型,
T
是类或接口类型,
S
命名与
T
命名的类或接口不相交的类或接口。
->
S 
T
命名相同的类
ArrayList
因此它们不是不相交的。

2。如果存在作为 T 的超类型的参数化类型 X 和作为 S 的超类型的参数化类型 Y,使得 X 和 Y 的擦除相同,则 X 和 Y 不可证明不同 (§4.5 ). ->

S
T
是同一类型
ArrayList
的参数化,所以问题是,
ArrayList<? extends Integer>
ArrayList<Number>
是否可以证明是不同的?让我们看看:

如果满足以下任一条件,则两个参数化类型可证明是不同的: (i) 它们是不同泛型类型声明的参数化。 (ii) 它们的任何类型参数都可证明是不同的。

这两种类型确实是同一泛型类型的参数化

ArrayList
。所以问题就变成了,类型参数
? extends Integer
Number
是否可以证明是不同的?规则如下:

如果满足以下条件之一,则两个类型参数可证明是不同的:(i)(不相关)。 (ii) 一个类型参数是类型变量或通配符,具有 S 的界限(如果是类型变量)或上限(如果是通配符,则使用捕获转换 (§5.1.10),如有必要);并且另一个类型参数 T 不是类型变量或通配符;也没有 |S| <: |T| nor |T| <: |S| (§4.8§4.10)。 或 (iii)(不相关)。

如您所见,第二条规则适用,因为我们有一个

? extends Integer
作为第一种类型,第二个类型
Number
既不是类型变量也不是通配符。第一种类型中的通配符的上限为
Integer
,它是
Number
的子类型。因此,根据这条规则,这两种类型参数不能证明是不同的。这样就满足了允许缩小引用转换的所有列出的约束。如果我没有算错的话。

下面是完整的示例,您可以复制粘贴来测试:

import java.util.*;

public class Main {

    public static void main(String[] args) {
        ArrayList<? extends Integer> l = getList();
        ArrayList<Number> ll = (ArrayList<Number>) l;
    }

    static ArrayList<? extends Integer> getList() { return null; }
}

奇怪的是,如果我们如下所示交换

S
(
Integer
) 中的上限和类型
T
(
Number
),编译器不会拒绝它:

 ArrayList<? extends Number> l = getList();
 ArrayList<Integer> ll = (ArrayList<Integer>) l;

如果有的话,第一个例子比这个更有意义。但是这一次通过了,而第一个没有通过。

我有可能误解了

JLS
规则,但如果我没有,我认为这将是编译器中的一个错误。对此你有何看法。

java generics language-lawyer jls
1个回答
0
投票

导入java.util.ArrayList; 导入 java.util.List;

公开课主课{

public static void main(String[] args) {
    // Create and populate a list of integers
    ArrayList<Integer> integerList = new ArrayList<>();
    integerList.add(1);
    integerList.add(2);
    integerList.add(3);

    // Assign integerList to a wildcard type that extends Integer
    ArrayList<? extends Integer> l = integerList;

    // Safely copy the elements to a new list with Number type
    ArrayList<Number> ll = new ArrayList<>();
    for (Integer num : l) {
        ll.add(num);  // Safe because num is an Integer, which is a subtype of Number
    }

    // Print the result
    System.out.println("Original list: " + l);
    System.out.println("Converted list: " + ll);
}

}

© www.soinside.com 2019 - 2024. All rights reserved.