为什么这些Java 8 lambda在类型转换期间的行为会有所不同?

时间:2019-02-02 23:53:44

标签: java java-8 java-stream

Stream st = Stream.of(1,2,3,4);

这会创建整数流(通过在运行时进行推断)还是对象流?

st.collect(Collectors.averagingInt((Integer x)->x));

此行编译正常。

st.forEach((Integer x)-> System.out.println(x));

此行不编译。它提供了在(整数x)编译错误,说预期目标,但发现整数。为什么?

为何不为上述averagingInt方法抱怨相同?

averagingInt采用原始参考类型为<? super T>的ToIntFunction接口,并允许使用Integer值。在forEach使用与<? super T>相同的原始引用类型的Consumer接口时,编译器在抱怨Integer参数值。

3 个答案:

答案 0 :(得分:4)

  

这是创建整数流(通过在运行时进行推断)还是对象流?

泛型在运行时不存在,没有什么可在运行时“推断”的。由于您为Stream st使用了原始类型-如果需要,它是Stream<Object>

好吧,即使是您写的东西(也确实可以编译):

st.collect(Collectors.averagingInt((Integer x)->x));

当您尝试添加预期结果时将无法编译:

Double i = st.collect(Collectors.averagingInt((Integer x) -> x));

我想知道您是否像下面这样重写将更有意义:

Collector<Integer, ?, Double> collector = Collectors.averagingInt((Integer x) -> x);
Double result = st.collect(collector); // does not compile

推论对Collectors.averagingInt有用,但是由于您将Stream用作原始数据-其他所有内容也都是原始数据。

答案 1 :(得分:2)

如上所述,您正在尝试使用原始Stream。如果要遍历Integer,则需要为Stream指定泛型。

例如

Stream<Integer> st = Stream.of(1,2,3,4);

然后它将进行编译。

这个问题等同于在旧样式的通用集合中进行迭代的问题

Collection values = Arrays.asList(1,2,3,4);
for(Integer v: values){
    System.out.println(v);
}

上面的代码无法编译,因为值是对象列表而不是整数。

对于 averagingInt ,即使您提供 String列表,它也会编译。例如

    Stream st = Stream.of("1","2","3","4");
    st.collect(Collectors.averagingInt((Integer x)->x));

但是,它将在运行时由于ClassCastException而失败。此处的区别在于averagingInt尝试将所有传入类型转换为特定类型,而forEach仅使用类型,因为这可能导致编译失败。

等价

    ToIntFunction<? super Integer> mapper = new ToIntFunction<Integer>(){
        @Override
        public int applyAsInt(Integer value) {
            return value;
        }
    };

    st.collect(Collectors.averagingInt(mapper));

匿名类将在averagingInt内部使用,在将args传递给applyAsInt方法之前,它将被强制转换为适当的类型(在我的示例中为Integer)

另一个有趣的事情

Stream st = Stream.of(1,2,3,4);

Consumer<String> stringConsumer = new Consumer<String>() {
    @Override
    public void accept(String o) {

    }
};

st.forEach(stringConsumer); // will compile disregarding to raw type of Stream and specific Consumer

st.forEach((Integer  a) -> System.out.println(a)); // will fail because os raw Stream 

答案 2 :(得分:1)

签名是不同的

Collectors.averagingInt接受ToIntFunction,其中操作collect return 类型绑定到Stream类型,即原始的。

Stream.forEach接受Consumer,该{<1>直接绑定到Stream的类型。

原始类型会干扰您在forEach中指定类型的能力,因此由于类型安全性,您将导致编译错误。