【问题标题】:Java Collection with generic method and subclasses具有泛型方法和子类的 Java 集合
【发布时间】:2017-03-26 17:19:00
【问题描述】:

我有以下集合类,它包含一个用于对地图中的元素进行分组的方法,其中每个值都具有调用它的类的类型

class TaskCollection<E extends Task> extends HashSet<E> {
    <K> Map<K, ? extends TaskCollection<E>> groupBy(Function<E, K> groupingFunction) {
        return this.stream()
            .collect(Collectors.groupingBy(
                    groupingFunction,
                    Collectors.toCollection(this.collectionConstructor())
            ));
    }

    Supplier<? extends TaskCollection<E>> collectionConstructor() {
        return TaskCollection::new;
    }
}

我想要的是能够创建使用groupBy 方法的子类,该方法将自身的新实例作为地图值返回。
下面是一个例子

class AssertionCollection extends TaskCollection<Assertion> {
    Map<Person, AssertionCollection> groupByPerson() {
        return this.groupBy(Assertion::assignedPerson);
    }

    @Override
    Supplier<AssertionCollection> collectionConstructor() {
        return AssertionCollection::new;
    }
}

问题出在groupByPerson 方法中。编译器为groupBy 调用抛出错误。

Error:(15, 28) java: incompatible types: no instance(s) of type variable(s) K exist so that java.util.Map&lt;K,? extends TaskCollection&lt;Assertion&gt;&gt; conforms to java.util.Map&lt;Person,AssertionCollection&gt;

我是 Java 新手,所以我很确定我没有看到一些愚蠢的东西

【问题讨论】:

  • 关于您的实现的评论:似乎TaskCollection 有一个HashSetTaskCollection 是一个HashSet 更有意义。 HashSet 的使用感觉像是一个实现细节。需要继承HashSet吗?

标签: java generics java-8


【解决方案1】:

意图是对于任何扩展TaskCollection 的类X,当执行groupBy 操作时,用于映射值的集合也是类X 的实例。

在这种情况下,您可以达到的最接近的情况如下所示:

class Task {}

class Assertion extends Task {}

abstract class TaskCollection<E extends Task, C extends TaskCollection<E, C>> extends HashSet<E> {

    <K> Map<K, C> groupBy(Function<E, K> groupingFunction) {
        return this.stream()
            .collect(Collectors.groupingBy(
                    groupingFunction,
                    Collectors.toCollection(this.collectionSupplier())
            ));
    }

    protected abstract Supplier<C> collectionSupplier();
}

class AssertionCollection extends TaskCollection<Assertion, AssertionCollection> {

    @Override
    protected Supplier<AssertionCollection> collectionSupplier() {
        return AssertionCollection::new;
    }
}

请注意,上面TaskCollection 的定义并没有完全阻止子类使用另一个TaskCollection 类作为它们的groupBy 映射值。例如,这也可以编译:

class AssertionCollectionOther extends TaskCollection<Assertion, AssertionCollectionOther> {...}

class AssertionCollection extends TaskCollection<Assertion, AssertionCollectionOther> {...}

不幸的是,至少目前无法施加这样的约束,因为您无法引用在 C 类型参数通配符中声明的类。

如果你可以假设后代有一个无参数构造函数作为集合供应商,你可以提供一个默认实现 collectionSupplier。您付出的代价是需要消除“未经检查”的警告(不是真正的问题),并且不兼容的类(不提供无参数构造函数)不会在编译时失败,但在运行时不太理想:

import java.util.function.*;
import java.util.*;
import java.util.stream.*;

class Task {}

class Assertion extends Task {}

class TaskCollection<E extends Task, C extends TaskCollection<E, C>> extends HashSet<E> {

    <K> Map<K, C> groupBy(Function<E, K> groupingFunction) {
        return this.stream()
            .collect(Collectors.groupingBy(
                    groupingFunction,
                    Collectors.toCollection(this.collectionSupplier())
            ));
    }

    @SuppressWarnings("unchecked")
    protected Supplier<C> collectionSupplier() {
       return () -> {
         try {
          return (C) this.getClass().newInstance();
         } catch (Exception ex) {
          throw new RuntimeException(String.format("class %s is not a proper TaskCollection", this.getClass()), ex);
         }
      };
    }
}

class AssertionCollection extends TaskCollection<Assertion, AssertionCollection> {
    // This override is not needed any longer although still could
    // be included in order to produce a slightly faster 
    // customized implementation:
    //@Override
    //protected Supplier<AssertionCollection> collectionSupplier() {
    //    return AssertionCollection::new;
    //}
}

如果您将collectionSupplier 声明为final,您将有效地强制子类始终返回它们自己的类的实例,但需要注意的是,诸如class AssertionCollection extends TaskCollection&lt;Assertion, AssertionCollectionOther&gt; 之类的声明仍然会编译并产生运行-时间转换异常。

【讨论】:

  • 这就是我想要的!谢谢
  • 我已经删除了我丑陋的答案。很好的答案,并投票。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-24
  • 2017-07-10
  • 1970-01-01
  • 2016-06-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多