What is the general principle behind "parameter type compatibility" (whatever we can call it)?
Class<Base> cls1 = new Derived().getClass(); // ERR
Class<? extends Base> cls2 = new Derived().getClass(); // OK
Class<? super Base> cl3 = new Derived().getClass(); // ERR
Class<Base> cls4 = new Base().getClass(); // ERR
Class<? extends Base> cls5 = new Base().getClass(); // OK
Class<? super Base> cls6 = new Base().getClass(); // ERR
My answer:
Class<Base> cls1
in LHS expects exactly Class<Base>
in RHS => Class<? extends Derived>
is error.
Class<? extends Base> cls2
as per PECS means that anything we get from cls2 (by iterator or (imagine it exists) "get" methods) is Base (so that we can call base methods on what we got). Class<? extends Derived>
is Base in any case (be it Derived or its subclasses). So it compiles.
Class<? super Base> cls3
as per PECS means that we can always add Base to cls3 by (imagine it exists) "add/put" method - so Collection contains Base or its superclass. Class<? extends Derived>
can never be Base => Error.
I understand that new Derived().getClass()
returns Class<? extends Derived>
, likewise new Base().getClass()
returns Class<? extends Base>
.
I understand that List<? extends String> lst = new ArrayList<String>();
is OK and why.
I understand array covariance and covariant return types.
But the above set of test problems beats me - I cannot formulate rules that my reasoning shall be guided by.
P.S. PECS answers don't help me because mainly they view the problem in terms of "can I add or put or both into a collection/object". The focus of my question is on references compatibility.
Given an expression of type T
, expression.getClass()
returns Class<? extends T>
, because expression
is either a T
or a subclass of T
. (Strictly, it is a Class<? extends |T|>
, where ||
denotes erasure; but that's unnecessary detail in the context of this question, where none of the values of T
are generic).
Given this fact, expression.getClass()
is neither Class<T>
nor Class<? super T>
, in the same way that a List<? extends Number>
isn't a List<Number>
or a List<? super Number>
.
It doesn't matter if something
is new SpecificSomething()
: that's not a Class<SpecificSomething>
, because the fact it's a new instance (and that it's guaranteed to be exactly a SpecificSomething
) isn't taken into account: new SpecificSomething()
is an expression of type SpecificSomething
, so getClass()
returns a Class<? extends SpecificSomething>
.