简体   繁体   English

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

[英]Why is Stream.sorted not type-safe in Java 8?

This is from the Stream interface from Oracle's implementation of JDK 8: 这是来自Oracle的JDK 8实现的Stream接口:

public interface Stream<T> extends BaseStream<T, Stream<T>> {
    Stream<T> sorted();
} 

and it is very easy to blow this up at run time and no warning will be generated at compile time. 并且很容易在运行时将其清除,并且在编译时不会生成警告。 Here is an example: 这是一个例子:

class Foo {
    public static void main(String[] args) {
        Arrays.asList(new Foo(), new Foo()).stream().sorted().forEach(f -> {});
    }
}

which will compile just fine but will throw an exception at run time: 这将编译得很好,但会在运行时抛出异常:

Exception in thread "main" java.lang.ClassCastException: Foo cannot be cast to java.lang.Comparable

What could be the reason that the sorted method was not defined where the compiler could actually catch such problems? 在编译器实际可以捕获这些问题的地方没有定义sorted方法的原因是什么? Maybe I am wrong but isn't it this simple: 也许我错了,但不是这么简单:

interface Stream<T> {
    <C extends Comparable<T>> void sorted(C c);
}

?

Obviously the guys implementing this (who are light years ahead of me as far as programming and engineering is considered) must have a very good reason that I am unable to see, but what is that reason? 显然,那些实现这一点的人(考虑到编程和工程方面,我在光明之前几年)必须有一个很好的理由,我无法看到,但这是什么原因?

Essentially, you're asking if there's a way to tell the compiler, " hey, this one method requires the type parameter match more specific bounds than defined at the class level ". 从本质上讲,你问是否有办法告诉编译器,“ 嘿,这一方法要求类型参数匹配比类级定义的更具体的边界 ”。 This is not possible in Java. 这在Java中是不可能的。 Such a feature may be useful but I'd also expect confusing and/or complicated. 这样的功能可能很有用,但我也期望混淆和/或复杂。

There's also no way to make Stream.sorted() type-safe with how generics is currently implemented; 还没有办法使Stream.sorted()类型安全,目前如何实现泛型; not if you want to avoid requiring a Comparator . 如果你想避免要求Comparator For instance, you were proposing something like: 例如,你提出的建议如下:

public interface Stream<T> {

    <C extends Comparable<? super T>> Stream<T> sorted(Class<C> clazz);

} // other Stream methods omitted for brevity

Unfortunately, there's no guarantee that Class<C> is assignable from Class<T> . 不幸的是,不能保证Class<C>可以从Class<T>分配。 Consider the following hierarchy: 考虑以下层次结构:

public class Foo implements Comparable<Foo> { /* implementation */ }

public class Bar extends Foo {}

public class Qux extends Foo {}

You can now have a Stream of Bar elements but try to sort it as if it was a Stream of Qux elements. 您现在可以拥有Stream of Bar元素,但尝试对其进行排序,就像它是一个Qux Stream元素一样。

Stream<Bar> stream = barCollection.stream().sorted(Qux.class);

Since both Bar and Qux match Comparable<? super Foo> 由于BarQux匹配Comparable<? super Foo> Comparable<? super Foo> there is no compile-time error and thus no type-safety is added. Comparable<? super Foo>没有编译时错误,因此没有添加类型安全性。 Also, the implication of requiring a Class argument is that it'll be used for casting. 此外,要求Class参数的含义是它将用于铸造。 At runtime this can, as shown above, still result in ClassCastException s. 在运行时,如上所示,这仍然可以导致ClassCastException If the Class isn't used for casting then the argument is completely useless; 如果Class 用于强制转换,则参数完全没用; I'd even consider it harmful. 我甚至认为它有害。

The next logical step is to try and require C extend T as well as Comparable<? super T> 下一个合乎逻辑的步骤是尝试要求C扩展T以及可Comparable<? super T> Comparable<? super T> . Comparable<? super T> For example: 例如:

<C extends T & Comparable<? super T>> Stream<T> sorted(Class<C> clazz);

This is also not possible in Java and results in a compilation error: "type parameter cannot be followed by other bounds". 这在Java中也是不可能的并且导致编译错误:“类型参数不能跟随其他边界”。 Even if this were possible, I don't think it'd solve everything (if anything at all). 即使这是可能的,我也不认为它会解决所有问题(如果有的话)。


Some related notes. 一些相关的说明。

Regarding Stream.sorted(Comparator) : It isn't the Stream that makes this method type-safe, it's the Comparator . 关于Stream.sorted(Comparator) :不是Stream使这个方法类型安全,它是Comparator The Comparator ensures the elements can be compared. Comparator确保可以Comparator元素。 To illustrate, the type-safe way to sort a Stream by the elements' natural order is: 为了说明,按元素的自然顺序对Stream进行排序的类型安全方法是:

Stream<String> stream = stringCollection.stream().sorted(Comparator.naturalOrder());

This is type-safe because naturalOrder() requires its type parameter extend Comparable . 这是类型安全的,因为naturalOrder()要求其类型参数extend Comparable If the generic type of the Stream did not extend Comparable then the bounds wouldn't match, resulting in a compilation error. 如果Stream的泛型类型未扩展Comparable则边界将不匹配,从而导致编译错误。 But again, it's the Comparator that requires the elements be Comparable * while the Stream simply doesn't care. 但同样, Comparator要求元素为Comparable *Stream不关心。

So the question becomes, why did the developers include a no-argument sorted method for Stream in the first place? 所以问题就变成了,为什么开发人员首先要为Stream包含一个无参数的sorted方法? It appears to be for historical reasons and is explained in an answer to another question by Holger. 这似乎是出于历史原因,并在霍尔格的另一个问题的答案中得到解释。


* The Comparator requires the elements be Comparable in this case. *在这种情况下, Comparator要求元素是可Comparable In general, a Comparator is obviously capable of handling any type it's defined to. 通常, Comparator显然能够处理它定义的任何类型。

The documentation for Stream#sorted explains it perfectly: Stream#sorted文档完美地解释了它:

Returns a stream consisting of the elements of this stream, sorted according to natural order. 返回由此流的元素组成的流,按照自然顺序排序。 If the elements of this stream are not Comparable, a java.lang.ClassCastException may be thrown when the terminal operation is executed. 如果此流的元素不是Comparable,则执行终端操作时可能会抛出java.lang.ClassCastException。

You're using the overloaded method that accepts no arguments (not the one that accepts a Comparator ), and Foo does not implement Comparable . 您正在使用不接受任何参数的重载方法(不是接受Comparator ),并且Foo不实现Comparable

If you're asking why the method doesn't throw a compiler error if the contents of the Stream do not implement Comparable , it would be because T is not forced to extend Comparable , and T cannot be changed without a call to Stream#map ; 如果你问为什么如果Stream的内容没有实现Comparable ,该方法不会抛出编译器错误,那将是因为T不被强制扩展Comparable ,并且如果没有调用Stream#map就无法更改T ; it seems to only be a convenience method so no explicit Comparator needs to be provided when the elements already implement Comparable . 它似乎只是一种方便的方法,因此当元素已经实现Comparable时,不需要提供显式的Comparator

For it to be type-safe, T would have to extend Comparable , but that would be ridiculous, as it would prevent a stream from containing any objects that aren't Comparable . 因为它是类型安全的, T必须扩展Comparable ,但这将是荒谬的,因为它会阻止流包含任何不可Comparable对象。

How would you implement that? 你会如何实现? sorted is a intermediate operation (can be called anywhere between other intermediate operations), meaning you can start with a stream that is not Comparable, but call sorted on one that is Comparable : sorted是一个中间操作(可以在其他中间操作之间的任何地方调用),这意味着你可以从一个不可比较的流开始,但是调用在 Comparable一个上sorted

Arrays.asList(new Foo(), new Foo())
      .stream()
      .map(Foo::getName) // name is a String for example
      .sorted()
      .forEach(f -> {});

The thing that you are proposing takes an argument as input, but Stream::sorted does not, so you can't do that. 你提议的东西是一个参数作为输入,但Stream::sorted不是,所以你不能这样做。 The overload version accepts a Comparator - meaning you can sort something by a property, but still return Stream<T> . 重载版本接受Comparator - 意味着您可以按属性排序,但仍返回Stream<T> I think that this is quite easy to understand if you would try to write your minimal skeleton of a Stream interface/implementation. 如果您尝试编写Stream接口/实现的最小骨架,我认为这很容易理解。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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