[英]Why is Stream.sorted not type-safe in Java 8?
这是来自Oracle的JDK 8实现的Stream接口:
public interface Stream<T> extends BaseStream<T, Stream<T>> {
Stream<T> sorted();
}
并且很容易在运行时将其清除,并且在编译时不会生成警告。 这是一个例子:
class Foo {
public static void main(String[] args) {
Arrays.asList(new Foo(), new Foo()).stream().sorted().forEach(f -> {});
}
}
这将编译得很好,但会在运行时抛出异常:
Exception in thread "main" java.lang.ClassCastException: Foo cannot be cast to java.lang.Comparable
在编译器实际可以捕获这些问题的地方没有定义sorted
方法的原因是什么? 也许我错了,但不是这么简单:
interface Stream<T> {
<C extends Comparable<T>> void sorted(C c);
}
?
显然,那些实现这一点的人(考虑到编程和工程方面,我在光明之前几年)必须有一个很好的理由,我无法看到,但这是什么原因?
从本质上讲,你问是否有办法告诉编译器,“ 嘿,这一方法要求类型参数匹配比类级定义的更具体的边界 ”。 这在Java中是不可能的。 这样的功能可能很有用,但我也期望混淆和/或复杂。
还没有办法使Stream.sorted()
类型安全,目前如何实现泛型; 如果你想避免要求Comparator
。 例如,你提出的建议如下:
public interface Stream<T> {
<C extends Comparable<? super T>> Stream<T> sorted(Class<C> clazz);
} // other Stream methods omitted for brevity
不幸的是,不能保证Class<C>
可以从Class<T>
分配。 考虑以下层次结构:
public class Foo implements Comparable<Foo> { /* implementation */ }
public class Bar extends Foo {}
public class Qux extends Foo {}
您现在可以拥有Stream
of Bar
元素,但尝试对其进行排序,就像它是一个Qux
Stream
元素一样。
Stream<Bar> stream = barCollection.stream().sorted(Qux.class);
由于Bar
和Qux
匹配Comparable<? super Foo>
Comparable<? super Foo>
没有编译时错误,因此没有添加类型安全性。 此外,要求Class
参数的含义是它将用于铸造。 在运行时,如上所示,这仍然可以导致ClassCastException
。 如果Class
不用于强制转换,则参数完全没用; 我甚至认为它有害。
下一个合乎逻辑的步骤是尝试要求C
扩展T
以及可Comparable<? super T>
Comparable<? super T>
。 例如:
<C extends T & Comparable<? super T>> Stream<T> sorted(Class<C> clazz);
这在Java中也是不可能的并且导致编译错误:“类型参数不能跟随其他边界”。 即使这是可能的,我也不认为它会解决所有问题(如果有的话)。
一些相关的说明。
关于Stream.sorted(Comparator)
:不是Stream
使这个方法类型安全,它是Comparator
。 Comparator
确保可以Comparator
元素。 为了说明,按元素的自然顺序对Stream
进行排序的类型安全方法是:
Stream<String> stream = stringCollection.stream().sorted(Comparator.naturalOrder());
这是类型安全的,因为naturalOrder()
要求其类型参数extend Comparable
。 如果Stream
的泛型类型未扩展Comparable
则边界将不匹配,从而导致编译错误。 但同样, Comparator
要求元素为Comparable
*而Stream
不关心。
所以问题就变成了,为什么开发人员首先要为Stream
包含一个无参数的sorted
方法? 这似乎是出于历史原因,并在霍尔格的另一个问题的答案中得到解释。
*在这种情况下, Comparator
要求元素是可Comparable
。 通常, Comparator
显然能够处理它定义的任何类型。
Stream#sorted
的文档完美地解释了它:
返回由此流的元素组成的流,按照自然顺序排序。 如果此流的元素不是Comparable,则执行终端操作时可能会抛出java.lang.ClassCastException。
您正在使用不接受任何参数的重载方法(不是接受Comparator
),并且Foo
不实现Comparable
。
如果你问为什么如果Stream
的内容没有实现Comparable
,该方法不会抛出编译器错误,那将是因为T
不被强制扩展Comparable
,并且如果没有调用Stream#map
就无法更改T
; 它似乎只是一种方便的方法,因此当元素已经实现Comparable
时,不需要提供显式的Comparator
。
因为它是类型安全的, T
必须扩展Comparable
,但这将是荒谬的,因为它会阻止流包含任何不可Comparable
对象。
你会如何实现? sorted
是一个中间操作(可以在其他中间操作之间的任何地方调用),这意味着你可以从一个不可比较的流开始,但是调用在可 Comparable
一个上sorted
:
Arrays.asList(new Foo(), new Foo())
.stream()
.map(Foo::getName) // name is a String for example
.sorted()
.forEach(f -> {});
你提议的东西是一个参数作为输入,但Stream::sorted
不是,所以你不能这样做。 重载版本接受Comparator
- 意味着您可以按属性排序,但仍返回Stream<T>
。 如果您尝试编写Stream接口/实现的最小骨架,我认为这很容易理解。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.