使用返回通用接口<t>的工厂的类会解析T的列表但不仅仅是T.

时间:2017-03-26 13:23:55

标签: java generics interface

我有以下设置。我有一个由AbstractFetcher<T>实施的StringFetcher。然后我有一个工厂根据输入返回AbstractFetcher ..这里OneClass正在使用AbstractFactoryFetcher。在方法doSomething中,我得到第二行的编译器错误,但不是第一行!换句话说,fetchMany()编译得很好!这是怎么回事?即使使用通用方法提示,如何更改签名,以便不需要进行转换?我的最终目标是将OneClass与关于具体获取者的知识分离,以便即使FetcherFactory上有更改也可以单独部署/编译。

public interface AbstractFetcher<T> {
    T fetchOne();
    List<T> fetchMany();
}

public class StringFetcher implements  AbstractFetcher<String> {

    @Override
    public String fetchOne() {
        return "a string"
    }

    @Override
    public List<String> fetchMany() {
        List<String> list = new ArrayList<>();
        list.add("String 1");
        list.add("String 2");
        return list;
    }
}

public interface AbstractFactoryFetcher {
    public AbstractFactoryFetcher make(String type);
}

public class FetcherFactory implements AbstractFactoryFetcher {
    public AbstractFetcher make(String type) {
        if (type.equals("String")) {
            return new StringFetcher();
        }
        else {
            return null;
        }
    }
}

public class OneClass {
    FetcherFactory factory;
    public OneClass(FetcherFactory factory) {
        this.factory = factory;
    }

    public void doSomething() {
        List<String> list = factory.make("String").fetchMany();
        String one = factory.make("String").fetchOne(); //compiler error Required: String, Found: Object
    }

}

谢谢!

3 个答案:

答案 0 :(得分:1)

您在AbstractFetcher中使用doSomething作为原始类型,这意味着使用了它的方法的擦除:

Object fetchOne();
List fetchMany();

虽然List可以转换为List<String>(带有未经检查的警告)。对象无法转换为String

最好是你有一个强类型的工厂:

public AbstractFetcher<String> makeStringFetcher() {
    return new StringFetcher();
}

您尝试将返回值用作AbstractFetcher<String>,这意味着您在编译时以任何方式知道type字符串。

泛型是一个编译时间,编译器无法知道type字符串的运行时值。因此,在这种情况下,您不能拥有通用类型安全性。

答案 1 :(得分:1)

但是在工厂里还有一个铸件:

public class FetcherFactory {
        public <T> AbstractFetcher<T> make(Class<T> type) {
            if (type.equals(String.class)) {
                return (AbstractFetcher<T>) new StringFetcher();
            }
            else {
                return null;
            }
        }
    }

然后你可以这样打电话:

public void doSomething() {
    List<String> list = factory.make(String.class).fetchMany();
    String one = factory.make(String.class).fetchOne(); 
}

答案 2 :(得分:1)

如果您可以将Class<X>而不是字符串参数传递给工厂,那么有一种方法可以在不进行任何强制转换的情况下实现此目的,但需要反射才能使其正常工作。

像这样实施FetcherFactory

public class FetcherFactory {
  public <T, U extends AbstractFetcher<T>> AbstractFetcher<T> make(Class<U> type) {
    try {
      return type.newInstance();
    } catch (InstantiationException | IllegalAccessException e) {
      throw new RuntimeException(e);
    }
  }
}

然后您可以在呼叫网站上使用它,如下所示:

public class OneClass {
  FetcherFactory factory;
  public OneClass(FetcherFactory factory) {
    this.factory = factory;
  }

  public void doSomething() {
    List<String> list = factory.make(StringFetcher.class).fetchMany();
    String one = factory.make(StringFetcher.class).fetchOne();
  }
}