This program does not compile:
public class xx {
static class Class1<C> {
void method1(C p) {
}
}
static class Class2<T> extends Class1<Class<? extends T>> {
T object;
void method2() {
this.method1(this.object.getClass());
}
}
}
The error is:
xx.java:10: method1(java.lang.Class<? extends T>) in xx.Class1<java.lang.Class<? extends T>>
cannot be applied to (java.lang.Class<capture#215 of ? extends java.lang.Object>)
this.method1(this.object.getClass());
Why does this happen? Why does the compiler seemingly believe that object.getClass() returns Class<? extends Object> instead of Class<? extends T> ?
There is no upper bound set on T in your code, so ? extends T is really tantamount to ? extends Object. Just yesterday I played with a similar example and hit this barrier. I had
static <T> T newInstance(T o) throws Exception {
final Class<? extends T> c = o.getClass();
return c.newInstance();
}
and it complained with the same error. Consider this: the return type of Object.getClass() is Class<?> and the compiler will want to capture the ? into a concrete type. But instead, we would like not to capture the ?, but to "capture the upper bound" T -- and there is no such thing in Java's generics.
Object.getClass() is defined to return a Class<? extends |T|>, where T is the statically known type of the receiver (the object getClass() is called on). Take special note of the vertical bars, the erasure operator. The erasure of a type variable is the erasure of its leftmost bound. In your case that's the implicit bound Object. So you get back a Class<? extends Object>, not a Class<? extends T>.
Why is that?
Imagine T = List<Integer>, you could suddenly do the following without unchecked warning:
List<String> myStrings = new ArrayList<>();
List<Integer> myInts = new ArrayList<>();
List<Integer> myIntyStrings = myInts.getClass().cast(myStrings);
myIntyStrings.add(-1);
String myString = myStrings.get(0); // BANG!
But thankfully we do get a warning.. ;)
According to the documentation on getClass(), the returned object has type Class< ? extends |X| >, where |X| is the erasure of the type of the instance on which the method is called.
Therefore calling getClass() on an object of type T returns Class< ? extends Object >. We have no bound information about T in this API.
Usually APIs which use reflection on generic classes require that the client pass an additional argument of type Class< T > to the constructor or generic method in question.
Related
Not sure how to put it in words, so I'll just start with the code I currently have:
// "root type" for all resources
// fixed
public class ResourceClassA
{ }
// "base type" for all resources for a specific domain
// fixed
public class ResourceClassB extends ResourceClassA
{ }
// specific resource type
// can be derived from further but don't think that matters here
// not fixed but heavily constrained in other ways
public class ResourceClassC extends ResourceClassB
{ }
// only needed for a negative example below, irrelevant otherwise
public class ResourceClassD extends ResourceClassB
{ }
// fixed
public class Remote
{
public <T extends ResourceClassA> Set<T> read(Class<T> baseType, Class<? extends T> subType);
}
// semi-fixed: read() signature can be modified
public interface AbstractRemoteAccess<T extends ResourceClassA>
{
Set<T> read(Class<? extends T> clazz);
}
// semi-fixed: read() signature can be modified
public class SpecificRemoteAccess<T extends ResourceClassA> implements AbstractRemoteAccess<T>
{
private Class<T> _baseType;
private Remote _remote;
public Set<T> read(Class<? extends T> clazz)
{
return _remote.read(_baseType, clazz);
}
}
// not fixed
public class ConsumerClass
{
public void doSomething(AbstractRemoteAccess<ResourceClassB> remoteAccess)
{
Set<ResourceClassB> rawObjects = remoteAccess.read(ResourceClassC.class);
Set<ResourceClassC> castedObjects = rawObjects.stream()
.map(c -> (ResourceClassC) c)
.collect(Collectors.toSet());
}
}
All classes marked with fixed cannot be changed, they are provided as is - vice versa for not fixed. Class SpecificRemoteAccess is the one I'm looking to change: I would like the read() method to
not return its result as Set but as a Set<> of generic type matching clazz
so that the caller does not have to cast the method's result, see ConsumerClass.doSomething()
and all of this without loosing type-safety
The easiest way I saw was to do
Set<V> read(Class<V extends T> clazz)
but that produces this error:
Incorrect number of arguments for type Class<T>; it cannot be parameterized with arguments <V, T>
which, if I'm interpreting it correctly, means the compiler is treating V & T as separate type arguments for Class which doesn't match its definition.
Next I tried adding a second generic type V and using it as generic type for the return type of read(). I started with
<V extends T> Set<V> read(Class<? extends T> clazz)
which doesn't constrain V to clazz at all, meaning both of these will be accepted by the compiler
Set<ResourceClassC> correct = remoteAccess.read(ResourceClassC.class);
Set<ResourceClassD> incorrect = remoteAccess.read(ResourceClassC.class);
The type declaration for incorrect is semantically wrong but syntactically fine. So I tried to constrain V based on clazz but the only solution I could think of is
<V extends T> Set<V> read(Class<V extends T> clazz, Class<V> classV)
which does, somewhat, fix the problem from above:
// compiles
Set<ResourceClassC> correct = remoteAccess.read(ResourceClassC.class,
ResourceClassC.class);
// error: Type mismatch: cannot convert from Set<ResourceClassC> to Set<ResourceClassD>
Set<ResourceClassD> incorrect = remoteAccess.read(ResourceClassC.class,
ResourceClassC.class);
but not only does it make the read() call cumbersome (users will be wondering why they have to pass the same info twice) but also error prone:
// compiles
Set<ResourceClassC> correct = remoteAccess.read(ResourceClassC.class,
ResourceClassC.class);
// type error
Set<ResourceClassD> incorrect = remoteAccess.read(ResourceClassC.class,
ResourceClassC.class);
// compiles but will cause run-time cast failures
Set<ResourceClassD> incorrect2 = remoteAccess.readAndCast2(ResourceClassC.class,
ResourceClassD.class);
Given consumer-side developers are faced with hundreds of resource classes like ResourceClassC, making read() error prone simply is no option.
Would appreciate if someone could point out my mistake.
Intuitively (and wrongly) I think that List<T> is not more specialized than List<? extends T> so below shall not compile (because return type covariance mandates that return type in Derived is same or subtype of that in Base) - but it compiles! I (wrongly) would expect it to compile if we change places of the methods.
But what is the strict (and correct) reasoning why this works?
class Base {
<T> List<? extends T> f1() {
return null;
}
}
class Derived extends Base {
<T> List<T> f1() { // fine !!! Not compile error.
return null;
}
}
Besides I misunderstand something - both methods have <T> - so could those two T's ever be different T ?
Why would this not compile? List<T> is a sub-type of List<? extends T>, i.e. this compiles just fine:
List<String> one = List.of("one");
List<? extends String> two = one;
It is also said that a wildcard with a bounded type, makes the type covariant.
I am facing a confusing issue in my code.
I have one method which signature is
public <T extends Measure> void sendNewMeasure(Class<T> type, T measure);
In another class, I have this method, which calls the previous one :
public <T extends Measure> void onNewMeasure(NewMeasureEvent<T> event) {
T measure = event.getMeasure();
APIInterface.getInstance().sendNewMeasure(measure.getClass(), measure);
}
The error I get is Wrong argument type, found 'T', required <? extends com.blablabla.Measure> but I don't get why, as the measure object is of type T which extends Measure.
Is there any way to fix this, and most importantly, why is it not working ?
Thanks !
EDIT :
This is the implementation of the sendNewMeasure method :
public <T extends Measure> void sendNewMeasure(Class<T> type, T measure) {
String measureType = measure.getJSONMeasureTypeName();
List<T> measures = T.find(type, measure.timestamp, true, false);
measures.add(measure);
sendMeasures(siteId, sensorId, measureType, measures);
}
EDIT 2 : And this is the find method signature, the one I cannot change:
public static <T> List<T> find(Class<T> type, int timestamp, boolean includeStart, boolean inclueEnd);
The type of .getClass() is not what you think it is. .getClass() returns Class<? extends |X|>, where |X| is the erasure of the static type of the expression it's called on. In this case, measure has static type T, which has erasure Measure, so measure.getClass() has type Class<? extends Measure>, i.e. the type parameter is an unknown subtype of Measure, and .sendNewMeasure(measure.getClass(), measure) there is no way the compiler can guarantee that measure (of type T) is an instance of this unknown type.
Basically, the problem is that .getClass() loses type information. Its return type is not directly linked to the type of the thing it is called on, because the Java type system cannot express the concept of "the real runtime type of the thing it's called on". However, intuitively, you know that the call to the method with the current signature .sendNewMeasure(measure.getClass(), measure) is type-safe, because the type of measure.getClass() should really be Class<U> where U is the real runtime class of measure, and you know that measure is obviously an instance of that same type U, so there exists some type argument, this U (which is not necessarily the same as T) for which the call to .sendNewMeasure() type-checks, but the question is how to convince the compiler of this without using unchecked operations.
The problem is that the type returned by measure.getClass() is not sufficiently linked to the type of measure. One way to re-link them is to use the class to cast the object to its type (which will always succeed), using the class's method .cast(). But it doesn't help to do this with an expression of type Class<? extends Measure>, because the resulting of .cast() is ? extends Measure which just degrades to Measure, so we still don't have a link between the two types. We need a real name for the type, not a wildcard, for us to maintain this link. The way to turn a wildcard into a named type is capture, which requires passing it into a generic method:
public <T extends Measure> void onNewMeasure(NewMeasureEvent<T> event) {
T measure = event.getMeasure();
helper(measure.getClass(), measure);
}
private <U extends Measure> void helper(Class<U> clazz, Measure measure) {
U castedMeasure = clazz.cast(measure);
APIInterface.getInstance().sendNewMeasure(clazz, castedMeasure);
}
From Java API for
Class<?> getClass()
[...]
The actual result type is Class<? extends |X|> where |X| is the erasure of the static type of the expression on which getClass is called.
So the result of measure.getClass() is not Class<T> but Class<? extends T>
Your signature should be:
public <T extends Measure> void sendNewMeasure(Class<? extends T> type, T measure);
I'm trying to figure out why this code does not compile on JDK 1.8.0_45:
public class Example<E extends Example<E>> {
public List<? extends Example<?>> toExamples(Collection<String> collection) {
return collection.stream()
.map(v -> lookup(v))
.collect(Collectors.toList());
}
public static <E extends Example<E>> E lookup(String value) {
return null;
}
}
Adding a seemingly unnecessary cast fixes it:
public class Example<E extends Example<E>> {
public List<? extends Example<?>> toExamples(Collection<String> collection) {
return collection.stream()
.map(v -> (Example<?>) lookup(v))
.collect(Collectors.toList());
}
public static <E extends Example<E>> E lookup(String value) {
return null;
}
}
Here's the error from the compiler:
Example.java:9: error: incompatible types: inference variable R has incompatible bounds
.collect(Collectors.toList());
^
equality constraints: List<Object>
upper bounds: List<? extends Example<?>>,Object
where R,A,T are type-variables:
R extends Object declared in method <R,A>collect(Collector<? super T,A,R>)
A extends Object declared in method <R,A>collect(Collector<? super T,A,R>)
T extends Object declared in interface Stream
For some reason, the return type of lookup() isn't correctly inferred to something extending Example.
As Peter Lawrey pointed out, ? extends Example<?> is not compatible with E extends Example<E>. Still, even fixing the signature doesn’t make type inference work here.
The reason is a known limitation of the type inference as it does not back-propagate through chained method invocations. In other words, the return type allows to infer the types for the collect(…) invocation but not for the preceding map(…) invocation. (see also this answer)
But it works for nested method invocations, so the following rewritten method can be compiled:
public class Example<E extends Example<E>> {
public <E extends Example<E>> List<E> toExamples(Collection<String> collection) {
return collection.stream()
.collect(Collectors.mapping(v -> lookup(v), Collectors.toList()));
}
public static <E extends Example<E>> E lookup(String value) {
return null;
}
}
Still, you have to rethink the semantics of your code. A method’s type parameter which appears only at the return type can’t be correct as it implies that “whatever the caller substitutes for this type parameter, the method will return the right thing”. Since the method implementation doesn’t know what the caller assumes, this is impossible. Only returning null or an empty list will work correctly, which is of little use.
When you have a ? it doesn't equal another ? i.e. the compiler doesn't see
? extends Example<?>
as a match for
E extends Example<E>
as it cannot assume the two ? are the same. It could be
A extends Example<B>
When you perform the cast, you obscure the constraint so it can match.
My guess is that the generic type defined in the static method is not the same as the generic type defined in the class. You should be able to make the lookup method non-static so it matches the same type defined in the class level generic declaration:
public E lookup(String value) {
return null;
}
From Effective Java Second Edition, Item 28 : "Do not use wildcard types as return types. Rather than providing additional flexibility for your users it would force them to use wildcard types in client code."
public final Class<? super T> getRawType()
I've just been getting to grips with generic wildcards to understand the last unchecked cast warning I have in a piece of code I am writing and I don't understand why getRawType() returns a wildcard type.
class Base<T>{}
class Child<T> extends Base<T>{}
public <C> void test (TypeToken<? extends Base<C>> token) {
Class<? extends Base<C>> rawType = (Class<? extends Base<C>>) token.getRawType();
}
I have to cast token.getRawType() as it returns a
Class<? super ? extends Base<C>>
What if you have a TypeToken<ArrayList<String>> and you want to get Class<ArrayList> (that is the raw type). If it returned Class<T>, then it would return Class<ArrayList<String>> which is not Class<ArrayList> that you want.
If the generic type of "token" is a Type class (i.e. if S in
TypeToken<S>
is a java.lang.reflect.Type class), then TypeToken.getRawType() will return the raw type associated to S. It shall be a Class object, parent of S.
See the source code of TypeToken.
In some cases (like having multiple bounds), the strategy implemented won't work and having a raw type is Ok: the raw type will be Object.class
See for instance MoreTypes:
public static Class<?> getRawType(Type type) {
...
} else if (type instanceof TypeVariable) {
// we could use the variable's bounds, but that'll won't work if there are multiple.
// having a raw type that's more general than necessary is okay
return Object.class;
} else {
...
}