Nếu câu hỏi là "Tôi có thể tránh chuyển đổi chuyển đổi trong khi chuyển đổi từ Stream<T>
sang IntStream
?" một câu trả lời có thể là "Không có cách nào trong Java để làm cho loại chuyển đổi an toàn như vậy và làm cho nó một phần của giao diện Stream
cùng một lúc".
Trên thực tế phương pháp mà chuyển đổi Stream<T>
-IntStream
mà không cần bộ chuyển đổi có thể được xem xét như thế này:
public interface Stream<T> {
// other methods
default IntStream mapToInt() {
Stream<Integer> intStream = (Stream<Integer>)this;
return intStream.mapToInt(Integer::intValue);
}
}
Vì vậy, nó giả được kêu gọi Stream<Integer>
và sẽ thất bại trên các loại khác của dòng suối. Nhưng bởi vì luồng được đánh giá lười biếng và do loại xóa (nhớ rằng Stream<T>
là chung chung) mã sẽ thất bại tại nơi dòng được tiêu thụ mà có thể là xa cuộc gọi mapToInt()
. Và nó sẽ thất bại theo cách cực kỳ khó xác định nguồn gốc của vấn đề.
Giả sử bạn có mã:
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);
}
}
Nó sẽ thất bại trên consumeIntStream()
cuộc gọi với:
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)
Có stacktrace này làm bạn có thể nhanh chóng xác định rằng vấn đề là ở produceIntStream()
vì mapToInt()
được gọi trên luồng của loại sai?
Tất nhiên người ta có thể viết phương pháp chuyển đổi đó là loại an toàn vì nó chấp nhận bê tông Stream<Integer>
:
public static IntStream mapToInt(Stream<Integer> stream) {
return stream.mapToInt(Integer::intValue);
}
// usage
IntStream intStream = mapToInt(Arrays.asList(1, 2, 3).stream())
nhưng nó không phải là rất thuận tiện vì nó phá vỡ giao diện bản chất thông thạo của suối.
BTW:
hàm mở rộng Kotlin của phép để gọi một số mã như nó là một phần của giao diện lớp. Vì vậy, bạn có thể gọi phương pháp này loại an toàn như phương pháp một Stream<java.lang.Integer>
's:
// "adds" mapToInt() to Stream<java.lang.Integer>
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) }
}
bạn đã xem xét sử dụng 'max' (Comparator naturalOrder().)? –
Và tại sao chính xác bạn nghĩ nó ngớ ngẩn? Bạn muốn trả về một int, vì vậy .mapToInt() có ý nghĩa ... – fge
@fge vì tôi nghĩ rằng tôi đang lãng phí một cuộc gọi đến autobox các giá trị, mà sẽ cần O (n) hoạt động. Tôi hy vọng tôi có thể lấy luồng dưới dạng IntStream trực tiếp – Hilikus