【问题标题】:Java Wildcard help Class<T> to List<Class<T>>, possible?Java 通配符帮助 Class<T> 到 List<Class<T>>,可能吗?
【发布时间】:2016-11-05 14:24:58
【问题描述】:

我很久没有编码了,对通配符失去了控制。

我有一个很好用的小功能

public static <T> ArrayList<T> GetObjects(List<?> list, Class<T> c){
    ArrayList<T> objects = new ArrayList();     
    for(Object o : list){
        if(c.isInstance(o)){
            objects.add(c.cast(o));
        }
    }       
    return objects;
}

但是我希望它能够接受类列表并执行类似的操作

public static <T> ArrayList<T> GetMultipleObjects(List<?> list, List<Class<T>> classes){
        ArrayList<T> objects = new ArrayList();     
        for(Object o : list){
            for(Class c : classes){
            if(c.isInstance(o)){
                objects.add(c.cast(o));
            }
            }
        }       
        return objects;
    }

事后看来,这显然是不可能的。

【问题讨论】:

  • 您对List&lt;Class&lt;T&gt;&gt; 有何期待? T 只有一个类。
  • 你会如何处理多个类?您只需列出所有不同类型的列表。
  • 还定义“不起作用”。目前我怀疑唯一的问题是在objects.add(c.cast(o)); 行中,因为c 是原始Class 而不是Class&lt;T&gt; 所以cast 返回Object 而不是T,这是ArrayList&lt;T&gt; objects 所期望的。
  • 我看到了我的问题,ArrayList。我将使用一个接口。
  • @TheOnlyWAR10CK93,不,不清楚。如果我通过Arrays.asList(Integer.class, String.class),我会得到什么? List&lt;Object&gt;?那有什么用呢?

标签: java list object search wildcard


【解决方案1】:

Louis 在 cmets 中是正确的:在 Java 中定义这个函数没有明智的方法。

Java 没有分离类型——也就是说,没有办法表达像“一个类型要么是String 要么是Integer”这样的概念。最接近的是找到一个共同的超类型——因为所有可实例化的类型都继承自Object(模原语,但现在让我们把它们放在一边),你总是可以保证能够找到一个共同的超类型,尽管它可能只需Object,这通常不是很有用。

为了说明为什么会这样,想想这个函数可以用于什么类型的事情。我看到两种情况:

案例 1:您想要过滤到一个类型是一个或多个类型的子类型,这些类型都共享一个共同的超类型,并返回超类型的列表。您可以通过将通配符类型与 type 参数一起使用来轻松做到这一点:

public static <T> List<T> filter(
    List<?> input,
    List<? extends Class<? extends T>> subtypesOfT) {
  // implementation exactly as you have it
}

但在大多数情况下,这将等同于仅使用原始函数并传递超类型。例如。使用您的原始函数,您只需传递Number.class 并返回List&lt;Number&gt;,其中列表中的值可能是DoubleIntegerFloat。唯一有用的方法是,如果您想从List&lt;?&gt; 中过滤掉List&lt;Number&gt;,但也只包含Number一些 子类型...例如你可以通过asList(Double.class, Integer.class) 并取回List&lt;Number&gt;。但我怀疑这是否特别有用,尤其是在这种一般形式中。

案例 2:或者,您可能希望获取 List&lt;?&gt; 并将其过滤为仅包含任意其他类型的值。在这种情况下,除非您计算一个,否则没有要统一的 T,所以您能做的最好的事情就是返回一个 List&lt;Object&gt;(或者,在这种情况下,如果列表是只读的,则返回一个 List&lt;?&gt; )。为此,您根本不需要该方法是通用的:只需使用 ArrayList&lt;Object&gt; 来构造返回值,它就可以正常工作。

如果您以足够的元编程方式执行此操作,您可能希望有一种方法来构建List&lt;T&gt;,其中TList&lt;?&gt; 中的所有值都可以是最具体的常见超类型分配给。这也是可行的,但您需要先找到T。这是您可以做到的一种方法:

private static Class<?> findCommonSupertype(List<Class<?>> types) {
  Class<?> result = types.get(0);
  for (Class<?> type : types) {
    // Keep going as long as result is a supertype of the current type.
    if (result.isAssignableFrom(type)) {
      continue;
    }
    // Skip interfaces since they unify with any class type (unless
    // the class is final, which we don't check for here).
    if (result.isInterface() && !type.isInterface()) {
      result = type;
    } else if (type.isInterface()) {
      continue;
    }
    // Otherwise, scan up through the inheritance hierarchy to find a
    // new type that can serve as a common supertype for type and result.
    Class<?> supertype = type;
    while (!supertype.isAssignableFrom(result)) {
      supertype = supertype.getSuperclass();
      if (supertype == null) {
        throw new IllegalArgumentException(
            type + " and " + result + " do not share a common supertype");
      }
    }
    result = supertype;
  }
  return result;
}

也就是说,即使你有这样的功能,它仍然不会特别有用,因为你可以构建一个List&lt;T&gt;,但你无法静态知道T 是什么(因为它会动态计算),所以我认为这也不是你真正想要的。

【讨论】:

  • 谢谢丹尼尔,自从我查看我的代码已经一年多了。我意识到我在完成 cmets 的过程中是多么愚蠢。不过感谢您的代码,其中有一些非常好的使用泛型类型的示例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-09
  • 1970-01-01
  • 1970-01-01
  • 2017-03-07
相关资源
最近更新 更多