这是来自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);

由于BarQux都与Comparable<? super Foo>匹配,因此没有编译时错误,因此没有添加类型安全性。另外,需要Class参数的含义是它将用于强制转换。在运行时,如上所示,这仍然会导致ClassCastException。如果不使用Class进行强制转换,则该参数完全没有用;我什至认为这有害。

下一步逻辑是尝试并要求C扩展TComparable<? super T>。例如:
<C extends T & Comparable<? super T>> Stream<T> sorted(Class<C> clazz);

在Java中这也是不可能的,并且会导致编译错误:“类型参数不能跟随其他界限”。即使这是可能的,我也不认为它可以解决所有问题(如果有的话)。

一些相关的注意事项。

关于Stream.sorted(Comparator):使得该方法类型安全的不是Stream,而是ComparatorComparator确保可以比较元素。为了说明这一点,按元素的自然顺序对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显然能够处理其定义为的任何类型。

07-24 09:49
查看更多