【问题标题】:Calling Overridden Default Method from Anonymous Inner Class从匿名内部类调用重写的默认方法
【发布时间】:2018-09-12 17:37:54
【问题描述】:

考虑这段代码:

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();
    }

}

这是人为的,但基本上Test::doA() 尝试将this 包装为B 并让B::doB() 调用其超级函数A.super.doA()

我可以在 B 类型的 lambda 中调用 A.super.doA() 就好了。但我无法弄清楚调用A.super.doA() 的语法 在匿名B 内。请参阅注释掉的代码。

有什么想法吗?

【问题讨论】:

  • 似乎不可能。一种解决方法是在匿名类之外的字段中捕获它: private Runnable runnable = A.super::doA;然后在匿名类中使用。

标签: java java-8 super anonymous-class default-method


【解决方案1】:

我认为这是不可能的。

这个:

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

或者这个:

A.super.doA();

是有效的,因为这些语句使用A 实例方法作为上下文。

对于匿名类,情况有所不同,因为您无权访问 A 实例上下文。 所以A不能被引用。
匿名类中的方法可以引用封闭方法的 final 变量或封闭方法的实例(通过前缀 classname.this),但该方法的行为不能像在 A 实例方法的上下文中执行一样:A.super.doA() 是什么意思。

我认为JLS的一部分必须指定这一点。

【讨论】:

  • 有趣。我本来希望能够有资格打电话给超级。类似于Test.this.A.super.doA()
【解决方案2】:

lambda 和匿名类中的代码被区别对待

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

this(显式和隐式)在 lambda 表达式主体中的透明度(即,将其与周围上下文中的处理方式相同)允许实现更大的灵活性,并防止正文中的非限定名称不依赖于重载决议。
实际上,lambda 表达式需要谈论自己是不寻常的(递归地调用自己或调用它的其他方法),而更常见的是希望使用名称来引用封闭类中的事物否则被遮蔽(this, toString())。如果 lambda 表达式需要引用自身(如通过 this),则应使用方法引用或匿名内部类。

JLS 10 - 15.27.2. Lambda Body

lambda 中的代码

关键字this 只能在 lambda 表达式出现的上下文中被允许使用在 lambda 表达式中。否则,会发生编译时错误。

JLS 10 - 15.8.3. this

我认为它也可以应用于关键字super

语句A.super.doA(); 将在封闭上下文(方法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();
    
        // ...
    }

}

这个 sn-p 打印

class Test
[interface A]

我们会将其与匿名类结果进行比较。

匿名类中的代码

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();
    }

}

sn-p 输出

class Test$1
[interface B]

请记住,匿名类有自己的 thissuper 并且它不继承 A(并且不能这样做),很明显 A.super.doA(); 不能在它的上下文。

解决方法

一种解决方法可能是通过 lambda 记住封闭上下文,并在匿名类的方法中调用该 lambda:

class Test implements A {

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

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

}

如果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();
    }

}

【讨论】:

  • 那么你能获得super吗?类似于这个工作的合格程度?我Test.super.doA() 似乎不起作用。
  • @AlexanderTorstling,Test 也不是new B() {...} 的父级,无法引用doA()
  • 否,但通常您可以使用限定的 this 来引用不同的“this:es”活动。我们都在new B() {...}new Test() 内部,所以我希望能够说出类似Test.this.A.super.doA() 或不那么疯狂的话来达到同样的效果。
【解决方案3】:

正如this answer 中所说,由于thissuper 的含义不同(与内部类相比),这在lambda 表达式中是可能的。

The Java® Language Specification, §15.12.1 明确解决了对内部类执行相同操作的可能性:

TypeName . super 语法被重载:传统上,TypeName 指的是一个词法封闭的类型声明,它是一个类,目标是此类的超类,就好像调用是词法封闭类型声明中的不合格 super

为了支持在超接口中调用默认方法,TypeName也可以直接引用当前类或接口的超接口,目标就是那个超接口。

没有语法支持这些形式的组合,即调用作为类的词法封闭类型声明的超接口方法,就好像调用的形式是 InterfaceName . @ 987654329@ 在词法封闭类型声明中。

class Subclass3 implements Superinterface {
    void foo() { throw new UnsupportedOperationException(); }

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

一种解决方法是在词法封闭类型声明中引入private 方法,该方法执行接口super 调用。

【讨论】:

    猜你喜欢
    • 2013-10-19
    • 2016-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-30
    • 1970-01-01
    • 1970-01-01
    • 2012-02-21
    相关资源
    最近更新 更多