Reputation: 35467
I have the following short, self-contained code that shows an error at compile time. I've desperately tried to have it compiling. I usually don't have trouble with generics anymore, but well I give up on this one and I ask the help of the team.
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
public class CacheWithGenericsDoesNotCompile {
// Class definition can't be modified
static class Resource<T extends Resource<T>> {}
// Class definition can't be modified
static class ResourceType<T extends Resource<T>> {
public ResourceType(Class<T> type) {}
}
// Variable definition may be modified
static LoadingCache<Class<? extends Resource<?>>, ResourceType<? extends Resource<?>>> cache = CacheBuilder.newBuilder().build(
new CacheLoader<Class<? extends Resource<?>>, ResourceType<? extends Resource<?>>>() {
@Override public ResourceType<? extends Resource<?>> load(Class<? extends Resource<?>> key) throws Exception {
return new ResourceType<? extends Resource<?>>(key);
}
});
// Method definition can't be modified, method content may.
@SuppressWarnings("unchecked")
static <T extends Resource<T>> ResourceType<T> getResourceType(Class<T> type) {
return (ResourceType<T>)cache.getUnchecked(type);
}
}
The line that fails to compile is:
return new ResourceType<? extends Resource<?>>(key);
I know why it fails: I may not write new Xxxx<...>
with a question mark (?
). I just can't write this line differently to have the other lines compiling.
I have a fallback solutions in the case where Resource
has no generics, but in the limit of the possible I'd like to keep Resource
with generics.
I have no restriction about the generics of the LoadingCache
except that I need it to be called as in getResourceType(Class)
.
So... how can I fix this code?
Upvotes: 3
Views: 2197
Reputation: 19682
If we can cast key
to a proper type
Class<T> for some T that T<:Resource<T>
then we have no problem to write
ResourceType<? extends Resource<?>> result = new ResourceType<>(casted_key)
Usually this kind of casting can be done through wildcard capture, but it doesn't work if the type variable has a self referential bound. See the workaround at Generics and Class<? extends Enum<?>>, EnumSet.allOf(class) vs class.getEnumConstants() .
Upvotes: 0
Reputation: 35467
I played a bit more with gontard's solution (so if you upvote this, make sure to upvote gontard's answer too) and millimoose's comment. I came to this solution which removes the generics completely, for a far more readable code.
@SuppressWarnings("rawtypes")
static LoadingCache<Class, ResourceType> cache = CacheBuilder.newBuilder().build(
new CacheLoader<Class, ResourceType>() {
@SuppressWarnings("unchecked")
@Override public ResourceType load(Class key) throws Exception {
return new ResourceType(key);
}
});
Upvotes: 1
Reputation: 29520
A solution with some ignored warning :
static LoadingCache<Class<? extends Resource<?>>, ResourceType<? extends Resource<?>>> cache = CacheBuilder.newBuilder().build(
new CacheLoader<Class<? extends Resource<?>>, ResourceType<? extends Resource<?>>>() {
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override public ResourceType<? extends Resource<?>> load(Class<? extends Resource<?>> key) throws Exception {
return new ResourceType(key);
}
});
As suggested by @John B in a comment. This code is not different at run-time than the code in the question due to type-erasure.
Upvotes: 5