开发者

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 of MyClass.
  • getInstance is declared to return an object of type T
  • 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);
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜