我尝试在Java中理解更基本的类型转换,但无法理解JLS的某些部分。
特别是这个(在类类型S到类或接口类型T的转换意义上):
此外,如果存在T的超类型X和S的超类型Y,使得X和Y都可证明是不同的参数化类型(§4.5),并且X和Y的擦除是相同的,发生编译时错误。
和this(在接口类型S到最终类类型T的转换意义上):
否则,S是参数化类型,它是一些泛型类型声明G的调用,或者是对应于泛型类型声明G的原始类型。那么必须存在T的超类型X,这样X是一个调用G,或发生编译时错误。 此外,如果S和X可证明是不同的参数化类型,则会发生编译时错误。
也许有人可以举一些简短的例子来澄清这些摘录?
P.S。经过@ErickGHagstrom的一些考虑和指导,我想我可以澄清这两个棘手的JLS摘录。
JLS说:
如果满足以下任一条件,则两个参数化类型可证明是不同的:
它们是不同泛型类型声明的参数化。
他们的任何类型参数都是明显不同的。
和
如果满足下列条件之一,则可以证明两个类型参数是不同的:
两个参数都不是类型变量或通配符,并且这两个参数的类型不同。
一个类型参数是一个类型变量或通配符,具有S的上限(来自捕获转换(第5.1.10节),如果需要);而另一个类型参数T不是类型变量或通配符;既不是| S | <:| T |也| T | <:| S | (§4.8,§4.10)。
每个类型参数都是一个类型变量或通配符,具有S和T的上限(如果需要,来自捕获转换);既不是| S | <:| T |也| T | &lt ;: | S |。
因此,List<Integer>
和List<Number>
可以区分,但List<? extends Integer>
和List<? extends Number>
不是(所有列出的类型都有相同的删除)。
关键是不能以任何方式实现具有相同擦除的两个可证明不同的参数化类型之间的子类型关系。
具有相同擦除的两个可证明不同的参数化类型的子类型之间的子类型关系也是不可能的。如果S <: List<Integer>
和T <: List<Number>
,那么即使理论上也无法进行铸件(T)S
和(S)T
。所以编译器抱怨。
以下是我提出的示例(编译并运行没有错误):
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
无需显式或隐式地实现{{1} }}。但是此S
必须才能实现与T
处于子类型关系的其他参数化接口类型。关键点在于参数化类型之间的子类型关系不仅通过扩展或实现来定义,还通过类型参数包含来定义。
答案 0 :(得分:0)
ArrayList<String>
与ArrayList<Integer>
具有相同的删除权限ArrayList
,但可证明是不同的,因为String
无法转换为Integer
而且Integer
可以投放到String
。
让 G 为List<T>
, S 为List<String>
。假设我有一个类MyFinalArrayList
,如此:
public final class MyFinalIntArrayList extends ArrayList<Integer> {
...
}
和另一个像
public final class MyFinalStrArrayList extends ArrayList<String> {
...
}
我可以将第一个转换为List<Integer
,将第二个转换为List<String
,但反之亦然。