How to avoid "Type mismatch" in static generic factory method?
Either I'm too stupid to use google, or nobody else encountered this problem so far.
I'm trying to compile the following code:
public interface MyClass {
public class Util {
private static MyClass _this;
public static <T extends MyClass> T getInstance(Class<T> clazz) {
if(_this == null) {
try {
_this = clazz.newInstance();
} catch(Exception e) {
e.printStackTrace();
}
开发者_JAVA技巧 }
return _this;
}
}
}
Howerer, in the line "return _this;" I get the error "Type mismatch: cannot convert from MyClass to T" Why is this? T extends MyClass, so where is the problem? If I change the line to "return (T)_this;", i just get a warning about the unchecked cast, but I don't like warnings ;-) Is there a way to achieve what i want without an error or warning?
Imagine you have two implementations of MyClass
, Foo
and Bar
. As a field of type MyClass
, _this
could be a Foo
or a Bar
.
Now, since your getInstance
method returns <T extends MyClass>
, it's legal to call it any of these ways:
MyClass myClass = Util.getInstance(MyClass.class);
This doesn't work if it's the first call, because MyClass
is an interface and can't be instantiated with newInstance()
.
Foo foo = Util.getInstance(Foo.class);
Bar bar = Util.getInstance(Bar.class);
Now, what would happen if _this
was an instance of Foo
and you called Util.getInstance(Bar.class)
? That's why you aren't allowed to do this.
That's because the variable _this
is of type MyClass
, not type T
. Even though it happens to contain an instance of T
, the compiler doesn't have a way of knowing that.
I just verified that this makes the compiler happy and still constrains types in the manner that you want:
public interface MyClass {
public class Util {
private static MyClass _this;
public static MyClass getInstance(Class<? extends MyClass> clazz) {
if(_this == null) {
try {
_this = clazz.newInstance();
} catch(Exception e) {
e.printStackTrace();
}
}
return _this;
}
}
}
Edit:
Thinking about the client code, this actually just exposes a bug in the design of this factory. Imagine this:
MyClass foo = MyClass.getInstance(Foo.class); // sets _this to a Foo and returns it
MyClass bar = MyClass.getInstance(Bar.class); // _this is already set to a Foo and
// we return a Foo when we probably
// are expecting a Bar!
The "Type Mismatch"...
...is due to the following:
T
represents a subclass ofMyClass
.getInstance
is declared to return an object of typeT
- It returns an object of type
MyClass
.
It's like declaring a method to return a Double
while it returns some Number
.
The solution...
... is to change the return statement to
return (T) _this;
(and add @SuppressWarnings("unchecked")
if you want to get rid of the warning).
But there's a problem...
As ColinD points out: Suppose you have
class MyClassImpl1 implements MyClass {
}
class MyClassImpl2 implements MyClass {
}
and do the following:
MyClassImpl1 o1 = MyClass.Util.getInstance(MyClassImpl1.class);
// _this now holds a value of type MyClassImpl1...
// ... which causes this line to throw a ClassCastException.
MyClassImpl2 o2 = MyClass.Util.getInstance(MyClassImpl2.class);
精彩评论