繁体   English   中英

为什么Stream.sorted在Java 8中不是类型安全的?

[英]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);

由于BarQux匹配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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM