捕获#1-of?超级 C 在通用接口中解释为 C
capture#1-of ? super C interpreted as C in generic interface
给出以下 类:
class A { public A a() {return new A();}; };
class B extends A { public B b() {return new B();} };
class C extends B { public C c() {return new C();} };
class D extends C { public D d() {return new D();} };
我想写一些 UnaryOperator
可以接受 A、B 和 C 的实例,但不能接受 D。
所以我选择声明引用类型是UnaryOperator<? super C>
。由于我使用的是 super
,这意味着它也可以接受 Object
.
的实例
UnaryOperator<? super C> op1 = arg -> arg.a(); // does not compile, arg could be Object
UnaryOperator<? super C> op2 = arg -> arg.b(); // does not compile, arg could be Object
UnaryOperator<? super C> op3 = arg -> arg.c(); // does compile
UnaryOperator<? super C> op4 = arg -> arg.d(); // this is not expected to compile
为什么 op1
使代码无法编译并显示消息
Type mismatch: cannot convert from A to C
和 op2
使代码无法编译并显示消息
Type mismatch: cannot convert from B to C
,
但是 op3
编译得很好,让我调用一个只在 C 中可用的方法?
Why does op1
makes the code fail to compile...?
当通用函数式接口被通配符参数化时,有不同的实例化可以满足通配符并产生不同的函数类型。例如,每个 1:
UnaryOperator<C> (function type C -> C);
UnaryOperator<B> (function type B -> B);
UnaryOperator<A> (function type A -> A);
UnaryOperator<Object> (function type Object -> Object);
是一个UnaryOperator<? super C>
。
有时,可以从上下文(例如 lambda 的参数类型)中获知函数类型。其他时候需要选一个:
UnaryOperator<? super C> op1 = (A arg) -> arg.a();
^
UnaryOperator<? super C> op2 = (B arg) -> arg.b();
^
...
如果您不选择一个,则使用边界 2:
UnaryOperator<? super C> op = arg -> arg.c(); // valid
其中 C
是一个边界,因此在这种情况下 lambda 表达式是 UnaryOperator<C>
。
1 - JLS 9.9. Function Types - the last paragraph.
2 - JLS 15.27.3. Type of a Lambda Expression - 如果T
是通配符参数化的函数式接口类型并且lambda表达式是隐式类型的,那么地面目标类型是非通配符参数化 T
.
@Oleksandr 已经告诉你编译失败的原因。我会告诉你为什么你不能使用UnaryOperator<? super C>
只接受A, B, C
而拒绝D
。
UnaryOperator<? super C>
并不意味着函数接受 ? super C
。
这意味着 UnaryOperator<T>
中的通用 T
是 ? super C
。
假设您有一个实例 UnaryOperator<? super C> uo
。
uo.apply(new A())
有意义吗?否,因为 UnaryOperator<B>
可分配给 UnaryOperator<? super C>
但不能接受 A
.
uo.apply(new B())
有意义吗?否,因为 UnaryOperator<C>
可分配给 UnaryOperator<? super C>
但不能接受 B
.
那么uo
能接受什么呢?答案是 C
,因为只有 C
可以分配给任何 ? super C
.
因此您的 UnaryOperator<? super C>
实际上与 UnaryOperator<C>
相同。尽管您的 IDE 可能会告诉您它接受 Object
,但它不会接受无法分配给 C
.
的对象
给出以下 类:
class A { public A a() {return new A();}; };
class B extends A { public B b() {return new B();} };
class C extends B { public C c() {return new C();} };
class D extends C { public D d() {return new D();} };
我想写一些 UnaryOperator
可以接受 A、B 和 C 的实例,但不能接受 D。
所以我选择声明引用类型是UnaryOperator<? super C>
。由于我使用的是 super
,这意味着它也可以接受 Object
.
UnaryOperator<? super C> op1 = arg -> arg.a(); // does not compile, arg could be Object
UnaryOperator<? super C> op2 = arg -> arg.b(); // does not compile, arg could be Object
UnaryOperator<? super C> op3 = arg -> arg.c(); // does compile
UnaryOperator<? super C> op4 = arg -> arg.d(); // this is not expected to compile
为什么 op1
使代码无法编译并显示消息
Type mismatch: cannot convert from A to C
和 op2
使代码无法编译并显示消息
Type mismatch: cannot convert from B to C
,
但是 op3
编译得很好,让我调用一个只在 C 中可用的方法?
Why does
op1
makes the code fail to compile...?
当通用函数式接口被通配符参数化时,有不同的实例化可以满足通配符并产生不同的函数类型。例如,每个 1:
UnaryOperator<C> (function type C -> C);
UnaryOperator<B> (function type B -> B);
UnaryOperator<A> (function type A -> A);
UnaryOperator<Object> (function type Object -> Object);
是一个UnaryOperator<? super C>
。
有时,可以从上下文(例如 lambda 的参数类型)中获知函数类型。其他时候需要选一个:
UnaryOperator<? super C> op1 = (A arg) -> arg.a();
^
UnaryOperator<? super C> op2 = (B arg) -> arg.b();
^
...
如果您不选择一个,则使用边界 2:
UnaryOperator<? super C> op = arg -> arg.c(); // valid
其中 C
是一个边界,因此在这种情况下 lambda 表达式是 UnaryOperator<C>
。
1 - JLS 9.9. Function Types - the last paragraph.
2 - JLS 15.27.3. Type of a Lambda Expression - 如果T
是通配符参数化的函数式接口类型并且lambda表达式是隐式类型的,那么地面目标类型是非通配符参数化 T
.
@Oleksandr 已经告诉你编译失败的原因。我会告诉你为什么你不能使用UnaryOperator<? super C>
只接受A, B, C
而拒绝D
。
UnaryOperator<? super C>
并不意味着函数接受 ? super C
。
这意味着 UnaryOperator<T>
中的通用 T
是 ? super C
。
假设您有一个实例 UnaryOperator<? super C> uo
。
uo.apply(new A())
有意义吗?否,因为UnaryOperator<B>
可分配给UnaryOperator<? super C>
但不能接受A
.uo.apply(new B())
有意义吗?否,因为UnaryOperator<C>
可分配给UnaryOperator<? super C>
但不能接受B
.
那么uo
能接受什么呢?答案是 C
,因为只有 C
可以分配给任何 ? super C
.
因此您的 UnaryOperator<? super C>
实际上与 UnaryOperator<C>
相同。尽管您的 IDE 可能会告诉您它接受 Object
,但它不会接受无法分配给 C
.