具有泛型方法和子类的 Java Collection

标签 java generics java-8

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

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<K,? extends TaskCollection<Assertion>> conforms to java.util.Map<Person,AssertionCollection>

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

最佳答案

意图是对于任何类 X延伸 TaskCollection ,当执行 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<Assertion, AssertionCollectionOther>仍然会编译并产生运行时转换异常。

关于具有泛型方法和子类的 Java Collection,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43032023/

相关文章:

java - 从 JSON HAL 响应解析 _embedded 项

java - 由于 BigDecimals 中的二进制表示形式导致数字不准确。我该如何解决它?

java - 在Ubuntu中运行Hadoop Jar

java - 实现一个调用接口(interface)方法的类?

java - 处理文件中的嵌套 for 循环 - java 8

java - Spring程序化事务管理警告?

javascript - TypeScript:TypedArray 的泛型类型定义

java - 关于通配符参数化类型的混淆

在局部变量上调用的 Java 8 方法引用

arraylist - 有没有办法使用Java 8中的数据流将由特定字符描绘的许多多行字符串收集到Arraylist中?