这是来自Oracle JDK 8实现的Stream接口(interface):
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
元素的Bar
,但尝试对其进行排序,就好像它是Stream
元素的Qux
一样。Stream<Bar> stream = barCollection.stream().sorted(Qux.class);
由于
Bar
和Qux
都与Comparable<? super Foo>
匹配,因此没有编译时错误,因此没有添加类型安全性。另外,需要Class
参数的含义是它将用于强制转换。在运行时,如上所示,这仍然会导致ClassCastException
。如果不使用Class
进行强制转换,则该参数完全没有用;我什至认为这有害。下一步逻辑是尝试并要求
C
扩展T
和Comparable<? super T>
。例如:<C extends T & Comparable<? super T>> Stream<T> sorted(Class<C> clazz);
在Java中这也是不可能的,并且会导致编译错误:“类型参数不能跟随其他界限”。即使这是可能的,我也不认为它可以解决所有问题(如果有的话)。
一些相关的注意事项。
关于
Stream.sorted(Comparator)
:使得该方法类型安全的不是Stream
,而是Comparator
。 Comparator
确保可以比较元素。为了说明这一点,按元素的自然顺序对Stream
进行排序的类型安全方法是:Stream<String> stream = stringCollection.stream().sorted(Comparator.naturalOrder());
这是类型安全的,因为
naturalOrder()
要求其类型参数扩展Comparable
。如果Stream
的通用类型未扩展Comparable
,则边界将不匹配,从而导致编译错误。但同样,是Comparator
要求元素为Comparable
*,而Stream
根本不在乎。所以问题就变成了,为什么开发人员首先要为
sorted
包含无参数的Stream
方法?它似乎是出于历史原因,由Holger在an answer to another question中进行了解释。*在这种情况下,
Comparator
要求元素为Comparable
。通常,Comparator
显然能够处理其定义为的任何类型。