创建一个集合以从通用方法返回

时间:2018-12-20 21:15:18

标签: java generics

注意:是的,我知道我可以使调用方法使用Collection而不是子类或强制转换Collection或其他一些东西。并且可能会在等待答案的同时这样做。但是我很好奇这是否可行,而我的Google-fu却无济于事。 :-)

所以我有一个方法,该方法以Set或List作为参数,并返回相同的新方法(是的,还有Collection的其他子类,但是我将看到这两个)。所以我尝试使用泛型:

private <T extends Collection<String>> T doStuff(T input) {
  T output = (input instanceof List) 
    ? new ArrayList<String>() 
    : new HashSet<String>();
  // do stuff to fill output from input
  return output;
}

此版本带有红色墨水,表示我无法将ArrayList转换为T。我对此进行了尝试,没有运气。有什么想法吗?

编辑:感谢您的好奇心。我当然会使用更简单的方法,例如只返回一个Collection,但是很高兴知道问题出在哪里,以备将来参考。

3 个答案:

答案 0 :(得分:2)

为避免编译错误,必须实例化类型T的对象,由于类型擦除,在Java中是不可能的。我建议使用两种重载方法,而不是通用方法。

答案 1 :(得分:0)

S extends Collection<String>是您要从方法中返回的集合的抽象类型,即ListSet

C extends S为返回结果的运行时类型,即ArrayListHashSet

为了在编译时获得C的实例,您将必须显式提供一个Class<C>实例作为参数。

因此,方法变为:

public <S extends Collection<String>, C extends S> S doStuff(S input, Class<C> clazz) throws Exception {
    C output = clazz.newInstance();
    // do stuff to fill output from input
    return output;
}

您可以使用以下方法调用此方法:

List<String> list = new ArrayList<String>() {{ add("Hello"); }};
doStuff(list, ArrayList.class);

Set<String> set = new HashSet<String>() {{ add("World"); }};
doStuff(set, HashSet.class);

当然,您应该注意可能会从clazz.newInstance()回调引发的检查异常。

答案 2 :(得分:-2)

如果您确实能够返回Collection,则意味着您只需要Collection接口,并且不必担心各种Collection子类的速度/大小之间的取舍。

如果确实是这样,那么无论调用哪个列表,都无论返回列表还是集合都没关系。所以只要这样做:

private Collection doStuff(Collection input) {
  // do stuff to fill output from input
  return new ArrayList(input);
  // or, return new HashSet(input);
}

如果您或您的调用者实际上确实在乎特定Collection子类的方法(或速度/大小保证),那么您将需要重载感兴趣的实际子类。

当然请注意,重载分辨率是在编译时使用静态(引用)类型确定的,而不是在运行时实际指向的动态类型。