如何避免静态通用工厂方法中的“类型不匹配”?

时间:2011-05-31 20:06:34

标签: java generics generic-method

要么我使用谷歌太愚蠢,要么到目前为止还没有其他人遇到过这个问题。

我正在尝试编译以下代码:

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;
    }
  }
}

然后,在“返回_this;”行中我收到错误“类型不匹配:无法从MyClass转换为T” 为什么是这样? T扩展了MyClass,问题出在哪里?如果我将线路改为“return(T)_this;”,我只是得到关于未经检查的演员的警告,但我不喜欢警告;-)有没有办法实现我想要的没有错误或警告?

4 个答案:

答案 0 :(得分:6)

想象一下,您有两个MyClassFooBar的实现。作为MyClass类型的字段,_this可以是FooBar

现在,由于您的getInstance方法返回<T extends MyClass>,因此以下列方式调用它是合法的:

MyClass myClass = Util.getInstance(MyClass.class);

如果它是第一次调用,则不起作用,因为MyClass是一个接口,无法使用newInstance()进行实例化。

Foo foo = Util.getInstance(Foo.class);
Bar bar = Util.getInstance(Bar.class);

现在,如果_thisFoo的实例并且您调用了Util.getInstance(Bar.class),会发生什么?这就是你不被允许这样做的原因。

答案 1 :(得分:4)

这是因为变量_this属于MyClass类型,而不是类型T。即使它恰好包含T的实例,编译器也无法知道这一点。

答案 2 :(得分:2)

我刚刚确认这会让编译器感到高兴,并且仍然以您想要的方式约束类型:

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;
    }
  }
}

编辑:

考虑客户端代码,这实际上只暴露了这个工厂设计中的一个错误。想象一下:

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!

答案 3 :(得分:1)

“类型不匹配”......

...是由于以下原因:

  • T代表MyClass
  • 的子类 声明
  • getInstance返回T
  • 类型的对象
  • 返回MyClass类型的对象。

就像声明一种方法在返回Double时返回Number

解决方案......

...是将return语句更改为

return (T) _this;

(如果你想摆脱警告,请添加@SuppressWarnings("unchecked")

但是有问题......

正如科林指出:假设你有

class MyClassImpl1 implements MyClass {
}

class MyClassImpl2 implements MyClass {
}

并执行以下操作:

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);