Given the following classes:
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();} };
I want to write some UnaryOperator
s that can accept instances of A, B and C, but not D.
So my choice for declaring the reference type is UnaryOperator<? super C>
. Since I'm using super
, it means it can also accept an instance of 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
Why does op1
makes the code fail to compile with a message
Type mismatch: cannot convert from A to C
and op2
makes the code fail to compile with a message
Type mismatch: cannot convert from B to C
,
but op3
compiles fine, and let me call a method available only in C?
Why does
op1
makes the code fail to compile...?
When a generic functional interface is parameterized by wildcards, there are different instantiations that could satisfy the wildcard and produce different function types. For example, each of 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);
is a UnaryOperator<? super C>
.
Sometimes, it's possible to known from the context, such as the parameter types of a lambda, which function type is intended. Other times, it is necessary to pick one:
UnaryOperator<? super C> op1 = (A arg) -> arg.a();
^
UnaryOperator<? super C> op2 = (B arg) -> arg.b();
^
...
If you don't pick one, the bounds are used 2:
UnaryOperator<? super C> op = arg -> arg.c(); // valid
Where C
is a bound, so the lambda expression is a UnaryOperator<C>
in this case.
1 - JLS 9.9. Function Types - the last paragraph.
2 - JLS 15.27.3. Type of a Lambda Expression - if T
is a wildcard-parameterized functional interface type and the lambda expression is implicitly typed, then the ground target type is the non-wildcard parameterization T
.