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);
精彩评论