[英]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>
由于
Bar
和Qux
匹配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.