对私有接口方法的方法引用

2022-09-01 18:15:14

请考虑以下代码:

public class A {
    public static void main(String[] args) {
        Runnable test1 = ((I)(new I() {}))::test;  // compiles OK
        Runnable test2 = ((new I() {}))::test;     // won't compile 
    }

    interface I {
        private void test() {}
    }
}

我真的不明白这一点...我知道这种方法是私有的。但是,如果我们将一个匿名类强制转换为其接口,会发生什么变化?更确切地说,我希望看到一个特定的JLS点,允许这个技巧。test()((I)(new I() {}))

附言我已将其报告为编译器的错误(ID:9052217)。在我看来,在这种特殊情况下应该很好地编译。Runnable test2 = ((new I() {}))::test;

附言到目前为止,根据我的报告创建了一个错误:https://bugs.openjdk.java.net/browse/JDK-8194998。它可能是关闭为“不会修复”或其他什么。


答案 1

这不是一个新问题,与私有接口方法或方法引用无关。

如果更改代码以扩展类而不是实现接口,并且调用该方法而不是引用它,则仍然会遇到完全相同的问题。

class A {
    public static void main(String[] args) {
        ((I)(new I() {})).test();  // compiles OK
        ((new I() {})).test();     // won't compile 
    }

    class I {
        private void test() {}
    }
}

但是,该代码可以应用于较旧的Java版本,我尝试了Java 9,8,7,6,5和1.4。都一样!!

问题是私有方法不是继承1,因此匿名类根本没有该方法。由于私有方法甚至不存在于匿名类中,因此无法调用它。

当您强制转换为 时,该方法现在存在供编译器查看,并且由于它是一个内部类,因此您将被授予访问权限(通过综合方法),即使它是私有的。II

在我看来,这不是一个错误。这就是私有方法在继承上下文中的工作方式。

1) 正如 Jorn VerneeJLS 6.6-5 中发现的:“[私有类成员] 不被子类继承”。


答案 2

private方法不是继承的(到目前为止我发现的最接近的是:JLS6.6-5“[私有类成员]不被子类继承”)。这意味着您无法从子类型访问私有方法(因为它根本不“具有”该方法)。例如:

public static void main(String[] args) {
    I1 i1 = null;
    I2 i2 = null;

    i1.test(); // works
    i2.test(); // method test is undefined
}

interface I1 {
    private void test() {}
}

interface I2 extends I1 {}

这也意味着您无法通过匿名子类的类型直接访问该方法。表达式的类型:test

(new I() {})

不是 ,但实际上是匿名子类的非可表示类型,因此您无法通过它进行访问。Itest

但是,表达式的类型:

((I) (new I() {}))

is(当您显式将其强制转换为 )时,因此您可以通过它访问该方法。(就像你可以在我上面的例子中做的那样)IItest((I1) i2).test();

类似的规则也适用于方法,因为它们也不是继承的。static


推荐