考虑 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.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);
}
}