【发布时间】:2016-01-10 11:19:45
【问题描述】:
我尝试更基本地理解 Java 中的类型转换,但无法理解 JLS 的某些部分。
特别是这个(在将类类型 S 转换为类或接口类型 T 的意思):
此外,如果存在 T 的超类型 X 和 S 的超类型 Y,使得 X 和 Y 都可证明是不同的参数化类型(第 4.5 节),并且 X 和 Y 的擦除相同,则发生编译时错误。
还有这个(将接口类型 S 转换为最终类类型 T):
否则,S 要么是参数化类型,它是对某个泛型类型声明 G 的调用,要么是对应于泛型类型声明 G 的原始类型。那么必须存在 T 的超类型 X,这样 X 就是一个调用G,或发生编译时错误。 此外,如果 S 和 X 可证明是不同的参数化类型,则会发生编译时错误。
也许有人可以举一些简短的例子来澄清这些摘录?
附:经过@ErickGHagstrom 的一些考虑和指导后,我想我可以澄清一下这两个棘手的 JLS 摘录。
提取 1。
JLS 说:
如果以下任一情况为真,则可证明两种参数化类型是不同的:
它们是不同泛型类型声明的参数化。
它们的任何类型参数都可以证明是不同的。
和:
如果满足以下条件之一,则可证明两个类型参数是不同的:
参数都不是类型变量或通配符,并且两个参数不是同一类型。
一个类型参数是类型变量或通配符,其上限(来自捕获转换(第 5.1.10 节),如有必要)为 S;并且另一个类型参数 T 不是类型变量或通配符;也没有|S| <:>
每个类型参数都是一个类型变量或通配符,具有 S 和 T 的上限(如果需要,来自捕获转换);也没有|S| <:>
因此List<Integer> 和List<Number> 可证明是不同的,但List<? extends Integer> 和List<? extends Number> 不是(所有列出的类型都有相同的擦除)。
关键点是两个可证明不同的具有相同擦除的参数化类型之间的子类型关系在任何情况下都是不可能的。
具有相同擦除的两个可证明不同的参数化类型的子类型之间的子类型关系也以任何方式都不可能。如果S <: List<Integer> 和T <: List<Number> 然后铸件(T)S 和(S)T 甚至在理论上都是不可能的。所以编译器会抱怨。
提取 2。
这是我想出的例子(编译和运行没有错误):
static final class T extends ArrayList<Number>{
}
static T t;
static List<?> l1 = new T();
static List<? extends Number> l2 = new T();
static List<String> l3;
public static void main(String[] args) {
t = (T)l1;
t = (T)l2;
// t = (T)l3; //error
}
您可以看到List<?> 和List<? extends Number> 可以转换为最终的T,而T 没有(也不能)实现这些接口中的任何一个。
T <: List<Number>。 List<Number> 是对List<E> 的调用,例如List<?> 和List<? extends Number>。 List<Number> 和 List<?> 和 List<? extends Number> 不能证明是不同的(见上文)。
如果我们想将参数化接口类型S 转换为最终类类型T,则T 不需要显式或隐式地实现S。但是这个T 必须 实现另一个与S 有子类型关系的参数化接口类型。关键是参数化类型之间的子类型关系不仅通过扩展或实现来定义,而且通过类型参数包含来定义。
【问题讨论】: