5

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();
        }
      }
      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?

4

4 回答 4

6

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.

于 2011-05-31T20:19:59.110 回答
4

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.

于 2011-05-31T20:10:32.997 回答
2

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!
于 2011-05-31T20:18:11.573 回答
1

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);
于 2011-05-31T20:16:41.157 回答