繁体   English   中英

Java 8收集与减少

[英]Java 8 collect vs reduce

众所周知,当进行累加时,“减少”总是返回一个新的不可变对象,而“收集”将对可变对象进行更改。

但是,当我不小心将一个方法引用分配给reduce方法和collect方法时,它编译时没有任何错误。 为什么?

看下面的代码:

public class Test {
    @Test
    public void testReduce() {

        BiFunction<MutableContainer,Long,MutableContainer> func =
            MutableContainer::reduce;

        // Why this can compile?
        BiConsumer<MutableContainer,Long> consume =
            MutableContainer::reduce;

        // correct way:
        //BiConsumer<MutableContainer,Long> consume =
        //  MutableContainer::collect;


        long param=10;

        MutableContainer container = new MutableContainer(0);


        consume.accept(container, param);
        // here prints "0",incorrect result,
        // because here we expect a mutable change instead of returning a immutable value
        System.out.println(container.getSum());

        MutableContainer newContainer = func.apply(container, param);
        System.out.println(newContainer.getSum());
    }
}

class MutableContainer {
    public MutableContainer(long sum) {
        this.sum = sum;
    }

    public long getSum() {
        return sum;
    }

    public void setSum(long sum) {
        this.sum = sum;
    }

    private long sum;

    public MutableContainer reduce(long param) {
        return new MutableContainer(param);
    }

    public void collect(long param){
        this.setSum(param);
    }
}

基本上,问题可以BiConsumer为: BiConsumer是一个函数接口,其函数声明如下:

void accept(T t, U u)

您为它提供了具有正确参数的方法引用,但返回类型错误:

public MutableContainer reduce(long param) {
    return new MutableContainer(param);
}

[该T参数实际上是this对象时reduce被调用时,由于reduce是一个实例方法而不是静态方法。 这就是参数正确的原因。]返回类型为MutableContainer而不是void 所以问题是,编译器为什么接受它?

直觉上,我认为这是因为方法引用或多或少等于一个看起来像这样的匿名类:

new BiConsumer<MutableContainer,Long>() {
    @Override
    public void accept(MutableContainer t, Long u) {
         t.reduce(u);
     }
}

请注意, t.reduce(u)将返回结果。 但是,结果将被丢弃。 由于可以用结果调用方法并丢弃结果,因此,我认为,通过扩展,这就是为什么对于方法返回void的功能接口,可以使用方法引用(方法返回结果)的原因。

从法律上讲 ,我相信原因是在JLS 15.12.2.5中 本节很困难,我还没有完全理解,但是在本节的某处说

如果e是精确的方法引用表达式,则R 2为空。

如果我正确阅读,则R 2是功能接口方法的结果类型。 我认为这是允许在需要void方法引用的地方使用非void方法引用的子句。

编辑:正如Ismail在评论中指出的那样, JLS 15.13.2在这里可能是正确的子句;它谈到方法引用与函数类型一致,而其中的条件之一就是函数类型的结果void 。)

无论如何,那应该有望解释它为什么编译 当然,编译器不能总是告诉您什么时候做会产生错误结果的事情。

暂无
暂无

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

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