如果问题是“在将
Stream<T>
转换为
IntStream
时,我能否避免传递转换器?”可能的答案之一是:“在Java中没有办法使这种转换具有类型安全性并同时成为
Stream
接口的一部分。”
实际上,将
Stream<T>
转换为
IntStream
的方法,如果不使用转换器,可能看起来像这样:
public interface Stream<T> {
default IntStream mapToInt() {
Stream<Integer> intStream = (Stream<Integer>)this;
return intStream.mapToInt(Integer::intValue);
}
}
这段代码应该在 Stream<Integer>
上调用,而在其他类型的流上则会失败。但由于流是惰性求值的,而且由于类型擦除(记住 Stream<T>
是泛型),代码将在消费流的地方失败,这可能远离 mapToInt()
调用处。它将以一种极难确定问题源头的方式失败。
假设你有以下代码:
public class IntStreamTest {
public static void main(String[] args) {
IntStream intStream = produceIntStream();
consumeIntStream(intStream);
}
private static IntStream produceIntStream() {
Stream<String> stream = Arrays.asList("1", "2", "3").stream();
return mapToInt(stream);
}
public static <T> IntStream mapToInt(Stream<T> stream) {
Stream<Integer> intStream = (Stream<Integer>)stream;
return intStream.mapToInt(Integer::intValue);
}
private static void consumeIntStream(IntStream intStream) {
intStream.filter(i -> i >= 2)
.forEach(System.out::println);
}
}
在调用consumeIntStream()
时,它将会失败并显示以下错误:
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at java.util.stream.ReferencePipeline$4$1.accept(ReferencePipeline.java:210)
at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
at java.util.stream.ForEachOps$ForEachOp$OfInt.evaluateSequential(ForEachOps.java:189)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.IntPipeline.forEach(IntPipeline.java:404)
at streams.IntStreamTest.consumeIntStream(IntStreamTest.java:25)
at streams.IntStreamTest.main(IntStreamTest.java:10)
有了这个堆栈跟踪,您能否快速确定问题在于produceIntStream()
,因为流的类型错误地调用了mapToInt()
?
当然,可以编写转换方法,它是类型安全的,因为它接受具体的Stream<Integer>
:
public static IntStream mapToInt(Stream<Integer> stream) {
return stream.mapToInt(Integer::intValue);
}
IntStream intStream = mapToInt(Arrays.asList(1, 2, 3).stream())
但这样做并不太方便,因为它会破坏流的流畅接口特性。
顺便说一下:Kotlin的扩展函数允许将某些代码调用作为类接口的一部分。因此,您可以将这个类型安全方法作为Stream 的方法来调用:
fun Stream<java.lang.Integer>.mapToInt(): IntStream {
return this.mapToInt { it.toInt() }
}
@Test
fun test() {
Arrays.asList<java.lang.Integer>(java.lang.Integer(1), java.lang.Integer(2))
.stream()
.mapToInt()
.forEach { println(it) }
}
max(Comparator.<Integer>naturalOrder())
吗? - Jon Skeet