为什么我们不能在 lambda 表达式中使用默认方法?

2022-08-31 21:02:42

我正在阅读Java 8上的本教程,其中作者展示了代码:

interface Formula {
    double calculate(int a);

    default double sqrt(int a) {
        return Math.sqrt(a);
    }
}

然后说

无法从 lambda 表达式中访问默认方法。以下代码无法编译:

Formula formula = (a) -> sqrt( a * 100);

但他没有解释为什么这是不可能的。我运行了代码,它给出了一个错误,

不兼容的类型:公式不是功能接口

那么为什么这是不可能的,或者错误的含义是什么?该接口满足具有一个抽象方法的功能接口的要求。


答案 1

这或多或少是一个范围问题。来自 JLS

与匿名类声明中出现的代码不同,lambda 主体中出现的名称和关键字的含义以及引用声明的可访问性与周围上下文中的含义相同(除了 lambda 参数引入了新名称)。thissuper

在您尝试的示例中

Formula formula = (a) -> sqrt( a * 100);

作用域不包含名称 的声明。sqrt

这在JLS中也有暗示

实际上,lambda 表达式需要谈论自身(以递归方式调用自身或调用其其他方法)是不寻常的,而更常见的是希望使用名称来引用封闭类中否则会被阴影(,)的事物。如果 lambda 表达式需要引用自身(就像通过此引用一样),则应改用方法引用或匿名内部类。thistoString()

我认为它本来是可以实施的。他们选择不允许这样做。


答案 2

Lambda 表达式的工作方式与匿名类完全不同,因为匿名类表示的内容与它在表达式周围的作用域中所表示的内容相同。this

例如,这会编译

class Main {

    public static void main(String[] args) {
        new Main().foo();
    }

    void foo() {
        System.out.println(this);
        Runnable r = () -> {
            System.out.println(this);
        };
        r.run();
    }
}

它打印的东西像

Main@f6f4d33
Main@f6f4d33

换句话说,是 一个 ,而不是由 lambda 表达式创建的对象。thisMain

因此,您无法在 lambda 表达式中使用,因为引用的类型不是 或子类型,并且没有方法。sqrtthisFormulasqrt

Formula虽然是一个功能接口,但代码

Formula f = a -> a;

编译并运行我没有任何问题。

虽然您不能为此使用 lambda 表达式,但您可以使用匿名类执行此操作,如下所示:

Formula f = new Formula() { 
    @Override 
    public double calculate(int a) { 
        return sqrt(a * 100); 
    }
};