I've created a factory class in my project which would allow me (in theory) to create managers for any (supported) given type. Interacting with the manager allows me to alter certain properties of a given type. The problem I'm facing is when I attempt to create a manager for a generic type, the compiler crushes my hopes and dreams.
The following code is a stripped down version of what I'm working with. The line where I attempt to create my 'test3Manager' will not compile and I'm trying to understand why this is the case. The lines below it shows a 'workaround', which I'm trying to avoid.
import java.util.List;
public class GenTest {
public static void main(String[] args) {
String test1 = "";
IRandomType<String> test2 = null;
IAnotherRandomType<?> test3 = null;
IManager<String> test1Manager = Factory.createManager(test1);
IManager<IRandomType<String>> test2Manager = Factory.createManager(test2);
IManager<IAnotherRandomType<?>> test3Manager = Factory.createManager(test3); // Doesn't compile. Why?
// Work around?
IManager<?> test3ManagerTmp = Factory.createManager(test3);
IManager<IAnotherRandomType<?>> test3Manager2 = (IManager<IAnotherRandomType<?>>) test3ManagerTmp;
}
public interface IRandomType<T> {}
public interface IAnotherRandomType<T> {}
public interface IManager<T> {}
public static class Factory {
public static <T> IManager<T> createManager(T object) {
return null;
}
}
}
The exact compile error message is:
Type mismatch: cannot convert from GenTest.IManager<GenTest.IAnotherRandomType<capture#1-of ?>> to GenTest.IManager<GenTest.IAnotherRandomType<?>>
Similar questions have been asked before (see below); however, I don't know if this question is considered a duplicate of them. I only state this since I'm having trouble inferring answers from these questions to mine. I'm hoping someone could clarify what I'm doing wrong with my use of generics.
Related questions on SO are:
Use the following:
IManager<IAnotherRandomType<?>> test3Manager =
Factory.<IAnotherRandomType<?>>createManager(test3);
This is just a case of the compiler's type inference falling on its face, so it's necessary explicitly provide the type argument for T
.
More technically:
test3
is declared to have the type IAnotherRandomType<?>
, where ?
is a wildcard capture - a sort of one-use type parameter representing some specific unknown type. That's what the compiler's referring to when it says capture#1-of ?
. When you pass test3
into createManager
, T
gets inferred as IAnotherRandomType<capture#1-of ?>
.
Meanwhile, test3Manager
is declared to have the type IManager<IAnotherRandomType<?>>
, which has a nested wildcard - it does not behave like a type parameter but rather represents any type.
Since generics aren't covariant, the compiler can't convert from IManager<IAnotherRandomType<capture#1-of ?>>
to IManager<IAnotherRandomType<?>>
.
More reading on nested wildcards:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With