【问题标题】:Why doesn't type inference work the same on lambdas and method references in Java?为什么类型推断在 Java 中的 lambda 和方法引用上的工作方式不同?
【发布时间】:2017-11-16 19:45:11
【问题描述】:

代码不应该编译,但它可以!

public class MyClass {

...........

    private void setEndDateOfValidStatusToCurrentTime(List<LifecycleStatus> oldStatuses, Date currentTime)
    {
        oldStatuses.stream()
            .filter(oldStatus -> isValidNow(oldStatus, currentTime))
            .findFirst().ifPresent(oldStatus -> oldStatus.setValidToDate(currentTime));
    }

    private boolean isValidNow(LifecycleStatus lifecycleStatus, Date currentTime)
    {
        Date start = lifecycleStatus.getValidFromDate();
        Date end = lifecycleStatus.getValidToDate();
        Date startTime = Optional.ofNullable(start).orElse(new Date(0L)); // BEGINNING OF TIME
        Date endTime = Optional.ofNullable(end).orElse(new Date(Long.MAX_VALUE)); // END OF TIME

        return startTime.before(currentTime) && endTime.after(currentTime);
    }

}

原因: 我在 lambda 中使用 isValidNow() 来定位 Predicate 接口,因为过滤器方法需要它。但是 isValidNow() 是一个 2 参数方法,而 Predicate 中的 test() 只需要 1 个参数!

我知道 Java 编译器具有类型推断的能力。有了这样的能力,智能编译器可能会在内部分解 isValidNow(),确定它可以安全地搁置第二个参数 (currentTime),并通过仅使用第一个参数 (oldStatus) 来提供满足 Predicate 中的 test() 的实现.

那么当我尝试使用方法引用时,为什么类型推断不起作用呢?有趣的是,如果我替换

filter(oldStatus -> isValidNow(oldStatus, currentTime)) 

filter(this::isValidNow) 

我看到了这些编译器错误:

- The method filter(Predicate<? super LifecycleStatus>) in the type Stream<LifecycleStatus> is not applicable for the arguments 
 (this::isValidNow)
- MyClass does not define isValidNow(LifecycleStatus) that is applicable here

【问题讨论】:

    标签: java lambda type-inference method-reference


    【解决方案1】:

    oldStatus -&gt; isValidNow(oldStatus, currentTime) 是这里的谓词/lambda,并且只接受一个参数。该 lambda 等效于:

    new Predicate<LifecycleStatus> {
        boolean test(LifecycleStatus oldStatus) {
            return isValidNow(oldStatus, currentTime);
        }
    }
    

    (其中currentTimelocal variable from the enclosing scope。)

    这肯定和this::isValidNow不一样,这就是后者无法编译的原因。

    【讨论】:

    • 你是说,为了符合单参数谓词方法 test(T t),在 lambda 表达式的右侧,我可以使用具有任意多个参数的方法,只要左边只有一个?
    • @softwarelover - lambda 是(在这种情况下)一个参数和一个表达式之间的映射。该表达式可以是任何你想要的(只要类型正确)。
    • 我在您的 Predicate 实现的等效代码中看到 oldStatus 作为参数传递。那么当前时间呢? test() 方法调用 isValidNow() 但 test() 如何知道 currentTime?我没有看到任何有关 currentTime 的声明。请澄清。
    • @softwarelover - 可能值得回顾:docs.oracle.com/javase/tutorial/java/javaOO/…(特别是“访问封闭范围的局部变量...”部分)。
    • 谢谢,但请将 currentTime 放在代码中的某个位置以使其完整。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多