Eli*_*nko 5 java generics compiler-errors wildcard java-8
给出以下两个类定义:
class C1<T extends C1<T>> {}
class C2<U> extends C1<C2<U>> {}
Run Code Online (Sandbox Code Playgroud)
以下类型声明:
C1<C2<?>> a;
Run Code Online (Sandbox Code Playgroud)
直观地认为声明的类型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
Run Code Online (Sandbox Code Playgroud)
(编辑:我在这里是一个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);
}
Run Code Online (Sandbox Code Playgroud)
所有这一切都很好,但如果我们尝试以下声明:
C5<C6<?, ?>> b;
Run Code Online (Sandbox Code Playgroud)
事情变得陌生.C6<?, ?>
是一个子类型C3<C6<?, ?>>
,因此根据我对上述关于声明的规范的解释,声明应该是有效的C1<C2<?>>
.问题是显然并非每个可能的子类型C6<?, ?>
实际上满足该约束,因此现在例如C5.accept()
将其参数类型解析为C6<?, ?>
,因此可以接受违反边界X
的参数,即参数化Y
和Z
不相同的任何参数.
我在哪里错了?我对子类型关系的理解是否不足?
(编辑:问题的以下部分仍然没有答案,但我已经把它移到了一个新的问题,因为这是一个完全不同的问题真的...抱歉弄得一团糟而且没有很好地使用网站哈哈...... )
除此之外,我在类似情况下也遇到了捕获转换的一些问题.采取以下类型声明:
C1<? extends C2<?>> c;
Run Code Online (Sandbox Code Playgroud)
与a
开头的类似声明不同,这在JDK-8u45中编译得很好.如果我们检查捕获转换规范,不过,看来这个声明应该导致编译时错误这一次.
特别是,新类型变量捕获的上限由下式CAP#T
给出glb(Bi, Ui[A1:=S1,...,An:=Sn])
,在这种情况下,Bi
解析为通配符绑定C2<?>
并Ui[A1:=S1,...,An:=Sn]
解析为C1<CAP#T>
.
由此,glb(C2<?>, C1<CAP#T>)
解析为交叉点型C2<?> & C1<CAP#T>
,这是无效的,因为C2<?>
和C1<CAP#T>
都是类类型,而不是接口类型,但其中没有一个是另一个的子类型.
我确定这不是一个错误而且我只是在某个地方犯了一些简单的错误......但是如果这里的任何人都没有对我有任何启示,我会尝试使用compiler-dev邮件列表或其他东西.
谢谢你的帮助!
然而
C2<x> extends C1<C2<x>>
对于任何引用类型x
,
情况并非如此
C2<?> extends C1<C2<?>>
通配符?
不是类型。它是一个类型参数。但语法非常具有欺骗性(有意为之)。
让我们使用不同的语法 - 如果有任何第一级通配符,请使用,{}
例如<>
List{?}, Map{String, ? extends Number}
Run Code Online (Sandbox Code Playgroud)
的含义{?}
是声明一个联合类型
List{? extends Number} == union of List<Number>, List<Integer>, List<Long>, ....
Run Code Online (Sandbox Code Playgroud)
很容易看出,List<Integer>
是 ; 的子类型 List{? extends Number}
。并且
List{? extends Number}
是一个子类型List{? extends Object}
但是,它不可能Foo{?}
是 a 的子类型Foo<x>
。
在我们的语法中,<>
保留用于用 types 替换类型变量。所以我们写
List<String>, C2<Integer>
等等。很容易理解它们的含义 - 只需将的源代码中的T
's 替换为,我们就得到了一个好旧的普通类。String
List
interface List<String>
String get(int)
Run Code Online (Sandbox Code Playgroud)
这不能用于通配符 - 它没有意义
interface List<?>
? get(int)
Run Code Online (Sandbox Code Playgroud)
所以这是不允许的new ArrayList{?}()
,或者class MyList implements List{?}
那么,我们该如何使用呢List{?}
?我们可以调用什么方法呢?
当表达式的类型为 a时List{?}
,我们知道它是一个对象,并且该对象必须属于List<x>
某个未知类型 x
的子类。这是通配符捕获
obj is a List{?} => obj is a List<x>, where x a subtype of Object.
Run Code Online (Sandbox Code Playgroud)
即使在编译时未知 的确切类型x
,我们仍然可以进行替换
interface List<x>
x get(int)
Run Code Online (Sandbox Code Playgroud)
这样我们就可以理解这个调用obj.get(0)
;它返回x
, 并且是;x
的子类型 Object
所以我们可以将返回值分配给 an Object
。
归档时间: |
|
查看次数: |
87 次 |
最近记录: |