简体   繁体   English

从匿名内部类中调用重写的默认方法

[英]Calling Overridden Default Method from Anonymous Inner Class

Consider this code: 考虑以下代码:

interface A {
    default void doA() {
        System.out.println("a");
    } 
}

interface B {
    void doB(); 
}

class Test implements A {

    @Override
    public void doA() {        
        // Works
        B b = () -> A.super.doA();
        b.doB();

        // Does not compile
        /*
        new B() {      
            public void doB() {  
                A.super.doA();
            }       
        }.doB();
        */
    }

    public static void main(String[] args) {
        new Test().doA();
    }

}

This is contrived, but basically Test::doA() tries to wrap this as a B and have B::doB() call its super function A.super.doA() . 这是做作,但基本Test::doA()尝试来包装thisB ,并有B::doB()调用它的超强功能A.super.doA()

I can call A.super.doA() in a lambda of type B just fine. 我可以在类型B的lambda中调用A.super.doA()很好。 But I cannot figure out the syntax of calling A.super.doA() inside an anonymous B . 但是我无法弄清楚在匿名B中调用A.super.doA()的语法。 See the commented out code. 请参阅注释掉的代码。

Any ideas? 有任何想法吗?

Code in lambdas and anonymous classes is treated differently Lambda和匿名类中的代码被不同地对待

Unlike code appearing in anonymous class declarations , the meaning of names and the this and super keywords appearing in a lambda body, along with the accessibility of referenced declarations, are the same as in the surrounding context (except that lambda parameters introduce new names). 与出现在匿名类声明中的代码不同 ,名称的含义以及出现在lambda正文中的thissuper关键字以及引用的声明的可访问性与周围环境相同 (除了lambda参数引入新名称)。

The transparency of this (both explicit and implicit) in the body of a lambda expression - that is, treating it the same as in the surrounding context - allows more flexibility for implementations, and prevents the meaning of unqualified names in the body from being dependent on overload resolution. 的透明度this在一个lambda表达式的主体(显式和隐) -即,将它与在周围的上下文-允许实现更大的灵活性,并防止不合格的名称在体内从依赖的含义在超载解决方案上。
Practically speaking, it is unusual for a lambda expression to need to talk about itself (either to call itself recursively or to invoke its other methods), while it is more common to want to use names to refer to things in the enclosing class that would otherwise be shadowed (this, toString()). 实际上,lambda表达式需要谈论自己(以递归方式调用其自身或调用其其他方法)是不寻常的,而更常见的情况是希望使用名称来引用封闭类中的内容,否则将被遮盖(这是toString())。 If it is necessary for a lambda expression to refer to itself (as if via this ), a method reference or an anonymous inner class should be used instead. 如果有必要让lambda表达式引用自身(如通过this ),则应改用方法引用或匿名内部类。

JLS 10 - 15.27.2. JLS 10-15.27.2。 Lambda Body λ体

Code in lambdas Lambdas中的代码

The keyword this may be used in a lambda expression only if it is allowed in the context in which the lambda expression appears. 仅当在lambda表达式出现的上下文中允许使用关键字this才能在lambda表达式中使用该关键字。 Otherwise, a compile-time error occurs. 否则,将发生编译时错误。

JLS 10 - 15.8.3. JLS 10-15.8.3。 this

I think it also can be applied to the keyword super . 我认为它也可以应用于关键字super

The statement A.super.doA(); 语句A.super.doA(); would be working in the enclosing context (the body of the method Test#doA ), so it is allowed in lambdas as well. 将在封闭上下文(方法Test#doA的主体)中工作,因此也可以在lambda中使用它。

class Test implements A {

    @Override
    public void doA() {
        B b = () -> {
            System.out.println(super.getClass());
            System.out.println(Arrays.toString(super.getClass().getInterfaces()));
        };
        b.doB();

        // ...
    }

}

This snippet prints 此摘要打印

class Test
[interface A]

We will compare it with the anonymous class result. 我们将其与匿名类结果进行比较。

Code in anonymous classes 匿名类中的代码

class Test implements A {

    @Override
    public void doA() {
        // ...

        new B() {
            public void doB() {
                System.out.println(super.getClass());
                System.out.println(Arrays.toString(super.getClass().getInterfaces()));
            }
        }.doB();
    }

}

The snippet outputs 片段输出

class Test$1
[interface B]

Keeping in mind that an anonymous class has own this and super and it does not inherit A (and can't do it), it becomes clear that A.super.doA(); 请记住,匿名类拥有thissuper并且它不继承A (并且不能继承),因此很明显, A.super.doA(); can't be compiled in its context. 无法在其上下文中进行编译。

Workarounds 解决方法

A workaround could be remembering the enclosing context by a lambda, and invoking that lambda in the method of an anonymous class: 一种解决方法是记住lambda所包含的上下文,并通过匿名类的方法调用该lambda:

class Test implements A {

    @Override
    public void doA() { 
        Runnable doA = () -> A.super.doA();

        new B() {
            public void doB() {
                doA.run();
            }
        }.doB();
    }

}

If B inherited A , it would be possible to call doA() or B.super.doA() referring to the default method: 如果B继承A ,这将是可以调用doA()B.super.doA()指的是默认方法:

class Test implements A {

    @Override
    public void doA() {
        new B() {
            public void doB() {
                doA(); // or B.super.doA();
            }
        }.doB();
    }

}

As said in this answer , this is possible in lambda expressions due to the different meaning of this and super (compared to inner classes). 就像在这个答案中说的那样,由于thissuper (与内部类相比)的含义不同,因此在lambda表达式中是可能的。

The impossibility to do the same with inner classes has been addressed in The Java® Language Specification, §15.12.1 explicitly: Java®语言规范§15.12.1中明确解决了内部类进行相同操作的可能性

The TypeName . TypeName . super syntax is overloaded: traditionally, the TypeName refers to a lexically enclosing type declaration which is a class, and the target is the superclass of this class, as if the invocation were an unqualified super in the lexically enclosing type declaration. super语法已重载:传统上, TypeName引用的是一个词法包围的类型声明,它是一个类,而目标是此类的超类,就好像调用是词法包围的类型声明中的不合格super

To support invocation of default methods in superinterfaces, the TypeName may also refer to a direct superinterface of the current class or interface, and the target is that superinterface. 为了支持超级接口中默认方法的调用, TypeName也可以引用当前类或接口的直接超级接口,并且目标是该超级接口。

No syntax supports a combination of these forms, that is, invoking a superinterface method of a lexically enclosing type declaration which is a class, as if the invocation were of the form InterfaceName . 没有语法支持这些形式的组合,即,调用作为类的词汇封装类型声明的超接口方法,就好像调用的形式为InterfaceName一样 . super in the lexically enclosing type declaration. 词汇表类型声明中的super

 class Subclass3 implements Superinterface { void foo() { throw new UnsupportedOperationException(); } Runnable tweak = new Runnable() { void run() { Subclass3.Superinterface.super.foo(); // Illegal } }; } 

A workaround is to introduce a private method in the lexically enclosing type declaration, that performs the interface super call. 一种解决方法是在词法包围的类型声明中引入private方法,该方法执行接口super调用。

I don't think that it is possible. 我认为不可能。

This one : 这个 :

B b = () -> A.super.doA();

or this one : 或这个:

A.super.doA();

are valid as these statements use the A instance method as context. 这些语句使用A实例方法作为上下文是有效的。

From the anonymous class, things are different as you don't have access to the A instance context. 与匿名类相比,情况有所不同,因为您无权访问A实例上下文。 So A cannot be referenced. 因此,不能引用A
The method in the anonymous class can reference final variables of the enclosing method or the instance of the enclosing method (by prefixing classname.this ) but the method cannot behave as if it was executed in the context of an A instance method : what A.super.doA() means. 匿名类中的方法可以引用封闭方法或封闭方法实例的final变量(通过给classname.this加上前缀),但是该方法无法像在A实例方法的上下文中执行那样操作:什么A.super.doA()意思。

I think that a section of the JLS has to specified this point. 我认为JLS的一部分必须指定这一点。

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

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