给定以下两个类定义:
class C1<T extends C1<T>> {}
class C2<U> extends C1<C2<U>> {}
以及以下类型声明:
C1<C2<?>> a;
直觉上感觉声明的类型 a
应该是有效的,但这不是 JDK-8u45 的行为方式。相反,我们得到类似以下输出的内容:
Test.java:3: error: type argument C2<?> is not within bounds of type-variable T
C1<C2<?>> a;
^
where T is a type-variable:
T extends C1<T> declared in class C1
1 error
(编辑:我在这里是一个 dingus,这部分已经回答:C2<?>
不扩展 C1<C2<?>>
。下面关于 c
声明的问题仍然是不过,开放性问题。)
但是 C2<?>
确实扩展了 C1<C2<?>>
,这似乎可以满足边界。据我所知,对 JLS 的检查没有提供进一步的说明。它真的应该像满足子类型关系的界限一样简单,因为 C2<?>
不是通配符类型,因此捕获转换只是参数的身份转换。
有些情况会变得不太清晰,例如,采用以下类定义:
class C3<T extends C3<?>> {}
class C4<Y, Z> extends C3<C4<Z, Y>> {}
class C5<X extends C3<X>> {
void accept(X x);
}
所有这些都很好,但是如果我们尝试以下声明:
C5<C6<?, ?>> b;
事情变得陌生了。 C6<?, ?>
是 C3<C6<?, ?>>
的子类型,因此根据我对上面给出的关于声明 C1<C2<?>>
的规范的解释,声明应该是有效的。问题是,显然不是 C6<?, ?>
的每个可能的子类型实际上都满足该边界,因此现在例如 C5.accept()
将其参数类型解析为 C6<?, ?>
并且因此可以接受违反 X
边界的参数,即 Y
和 Z
的参数化不是 67915 的任何参数相同。
我哪里错了?是不是我对亚型关系的理解不够?
(编辑:问题的以下部分仍未得到解答,但我已将其移至新问题 here,因为这是一个完全不同的问题,真的...很抱歉搞得一团糟没有很好地使用该网站哈哈......)
除此之外,我在类似情况下的捕获转换也遇到了一些问题。采用以下类型声明:
C1<? extends C2<?>> c;
与开头类似的声明 a
不同,这在 JDK-8u45 中编译得很好。但是,如果我们检查 specification for capture conversion,这次声明应该会导致编译时错误。
特别是,新类型变量捕获 CAP#T
的上限由 glb(Bi, Ui[A1:=S1,...,An:=Sn])
给出,在本例中,Bi
解析为通配符绑定(bind) C2<?>
,而 Ui[A1:=S1,...,An:=Sn]
解析为 C1<CAP#T>
。
由此, glb(C2<?>, C1<CAP#T>)
解析为交集类型 C2<?> & C1<CAP#T>
,这是无效的,因为 C2<?>
和 C1<CAP#T>
都是类类型,而不是接口(interface)类型,但它们都不是另一个的子类型。
这种(明显的)规则违规在 definition of the intersection type 本身中变得更加清楚。
我确定这不是一个错误,我只是在某个地方犯了一些简单的错误……但如果这里没有人能为我阐明这一点,我会尝试编译器开发邮件列表或其他东西。
感谢您的帮助!
最佳答案
同时
C2<x> extends C1<C2<x>>
对于任何引用类型 x
,
事实并非如此
C2<?> extends C1<C2<?>>
通配符 ?
是不是一种类型。它是一个类型参数。虽然语法非常具有欺骗性(设计使然)。
让我们使用不同的语法 - 如果有任何一级通配符,请使用 {}
而不是 <>
,例如
List{?}, Map{String, ? extends Number}
{?}
的含义就是声明一个联合类型
List{? extends Number} == union of List<Number>, List<Integer>, List<Long>, ....
很容易看出,List<Integer>
是 List{? extends Number}
的子类型;和
List{? extends Number}
是 List{? extends Object}
的子类型
但是,这不可能Foo{?}
是 Foo<x>
的子类型.
在我们的语法中,<>
保留用于用 types 替换类型变量。所以我们写
List<String>, C2<Integer>
等。很容易理解它们的含义 - 只需替换 T
与String
在 List
的源代码中,我们得到了一个很好的老式普通类。
interface List<String>
String get(int)
这不能用于通配符——它没有意义
interface List<?>
? get(int)
所以不允许new ArrayList{?}()
, 或 class MyList implements List{?}
那么,我们如何使用List{?}
?我们可以调用哪些方法?
当表达式的类型是List{?}
,我们知道它是一个对象,而且这个对象一定属于List<x>
的子类对于一些未知的类型 x
.这是通配符捕获
obj is a List{?} => obj is a List<x>, where x a subtype of Object.
即使 x
的确切类型在编译时是未知的,我们仍然可以进行替换
interface List<x>
x get(int)
所以我们可以理解调用 obj.get(0)
;它返回 x
, 和 x
是 Object
的子类型;所以我们可以将返回值分配给 Object
.
关于java - Java 中的参数化良构和捕获转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30779745/