【问题标题】:Type erasure in streams在流中键入擦除
【发布时间】:2019-06-04 15:21:56
【问题描述】:

我不是 Java 8 的新手,我知道流是一种非常强大、方便的数据处理方式,而且我经常使用它们。事实上,我正在使用流进行更复杂的数据处理而没有任何问题,但是看看这个?它看起来很基本的处理,但是类型擦除存在问题。为什么它不能处理这样的数据收集?

Set[] arrOfSets = {new TreeSet<>(List.of(1, 2)), new LinkedHashSet<>(Arrays.asList(1, 2, 3, null))};

/*why type erasure wins here? */
List<Number> collect = (List<Number>) Arrays.stream(arrOfSets).flatMap(Set::Stream).collect(Collectors.toList());

Object collect2 = Arrays.stream(arrOfSets).flatMap(set -> set.stream()).collect(Collectors.toList());
System.out.println("Collect2:" + collect2); /*[1, 2, 1, 2, 3, null]*/

/*Here type interference works fine*/
List<Integer> collect1 = new TreeSet<>(Arrays.asList(1, 2, 3)).stream().collect(Collectors.toList());

System.out.println(collect2.getClass().getSimpleName()); //ArrayList

List&lt;Set&gt;相同:

List<?> collect3 = listOfSets.stream().flatMap(Set::stream).collect(Collectors.toList());

运行时异常:java:不兼容类型:java.lang.Object 无法转换为 java.util.List

我觉得这是流的巨大缺点。在未来的 sdk 中是否有机会改善 Streams API 中的类型干扰,或者没有办法克服这个问题?

【问题讨论】:

  • Set[] arrOfSets - 你已经打破了类型推断,Sets 包含什么通用数据类型?而且您可能想使用Set::stream 而不是Set::Stream,对吧?

标签: java generics java-stream type-inference type-erasure


【解决方案1】:

问题是您使用的是没有泛型类型的原始Set[]

要使您的代码正常工作,您应该使用通用数组:

Set<Number>[] arrOfSets = new Set[]{
        new TreeSet<>(List.of(1, 2)), 
        new LinkedHashSet<>(Arrays.asList(1, 2, 3, null))
};
List<Number> collect = Arrays.stream(arrOfSets) // Stream<Set<Number>>
        .flatMap(Set::stream) // Stream<Number>
        .collect(Collectors.toList());

或使用Stream.map() 将您的流从Stream&lt;Object&gt; 投射到Stream&lt;Number&gt;

Set[] arrOfSets = new Set[]{
        new TreeSet<>(List.of(1, 2)), 
        new LinkedHashSet<>(Arrays.asList(1, 2, 3, null))
};
List<Number> collect = Arrays.stream(arrOfSets) // Stream<Set>
        .map(i -> (Set<Number>) i) // Stream<Set<Number>>
        .flatMap(Set::stream) // Stream<Object>
        .collect(Collectors.toList());

正如您已经提到的,List&lt;Set&gt; 的问题是相同的。 Set 没有类型,所以要么使用 List&lt;Set&lt;Number&gt;&gt; 要么像以前一样强制转换。 我建议使用键入的 Lists 或 Sets。

【讨论】:

  • 无论如何为什么它不应该是一个带有Set&lt;? extends Number&gt;[]的数组?我的意思是 TreeSet 不是 Set&lt;Number&gt; 的子类型
  • 是的,您也可以使用Set&lt;? extends Number&gt;[]Set&lt;Integer&gt;[]。他们都应该工作。
  • @SamuelPhilipp 您认为您正在创建 TreeSet&lt;Integer&gt; 的假设是错误的。您已使用“菱形运算符”指定new TreeSet&lt;&gt;(List.of(1, 2)),允许编译器从上下文推断类型,因此允许推断Number 作为元素类型。例如。 Set&lt;Set&lt;Number&gt;&gt; setOfSets = Set.of(new TreeSet&lt;&gt;(List.of(1, 2)), new LinkedHashSet&lt;&gt;(Arrays.asList(1, 2, 3, null))); 也可以。但一般来说,泛型和数组don’t play together;一直都是这样。
  • @SamuelPhilipp 我的错,我想定位 @Schroedinger 实际上,关于 this comment 但完成插入了你的名字(都以 S 开头),我看起来不够近。
  • @Schroedinger 是的,关于数组和缺少类型安全性,他是对的。我想解决你最初的困惑。所以你可以写Set&lt;Set&lt;Number&gt;&gt; setOfSets = Set.of(new TreeSet&lt;&gt;(List.of(1, 2)), new LinkedHashSet&lt;&gt;(Arrays.asList(1, 2, 3, null)));,但不能写Set&lt;Set&lt;Number&gt;&gt; setOfSets = Set.of(new TreeSet&lt;Integer&gt;(List.of(1, 2)), new LinkedHashSet&lt;&gt;(Arrays.asList(1, 2, 3, null)));。而原始类型数组接受任何东西。所以解决方案不是对泛型类型使用数组,而是List.of(…)Set.of(…) 或只是Stream.of(…),当你无论如何都要流式传输它们时。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多