考虑以下代码:
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()
。
我可以在A.super.doA()
类型的lambda中调用B
就好了。但我无法弄清楚调用A.super.doA()
的语法
在匿名B
内。请参阅注释掉的代码。
有什么想法吗?
答案 0 :(得分:2)
与匿名类声明中出现的代码不同,名称的含义以及出现在lambda主体中的
this
和super
关键字以及引用声明的可访问性, 与周围上下文相同(除了lambda参数引入新名称)。lambda表达式主体中
this
(显式和隐式)的透明度 - 也就是说,将其视为与周围上下文相同 - 允许实现更灵活,并防止身体中不合格名称的含义不依赖于超载分辨率 实际上,lambda表达式需要谈论自身(要么递归地调用自身还是调用其他方法)是不寻常的,而更常见的是想要使用名称来引用封闭类中的内容。否则被遮蔽(this,toString())。如果lambda表达式需要引用它自己(就像通过this
),则应该使用方法引用或匿名内部类。
只有在lambda表达式出现的上下文中允许使用关键字
this
时,才能在lambda表达式中使用关键字super
。否则,发生编译时错误。
我认为它也可以应用于关键字A.super.doA();
。
语句Test#doA
将在封闭的上下文(方法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();
// ...
}
}
的主体)中工作,因此也允许在lambdas中使用。
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();
}
}
我们会将它与匿名类结果进行比较。
class Test$1
[interface B]
代码段输出
this
请记住,匿名类拥有super
和A
并且它不会继承A.super.doA();
(并且无法执行),很明显{{1} 1}}无法在其上下文中进行编译。
解决方法可能是通过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();
}
}
答案 1 :(得分:2)
如this answer中所述,由于this
和super
的含义不同(与内部类相比),因此在lambda表达式中这是可能的。
The Java® Language Specification, §15.12.1明确地解决了对内部类做同样事情的不可能性:
TypeName
.
super
语法被重载:传统上, TypeName 是指一个词汇封闭类型声明,它是一个类,并且target是此类的超类,就好像在词法封闭类型声明中调用是非限定super
一样。...
为了支持在超接口中调用默认方法, TypeName 也可以引用当前类或接口的直接超接口,目标是超接口。
...
没有语法支持这些表单的组合,即调用词法封闭类型声明的超接口方法,这是一个类,就好像调用的形式是 InterfaceName {{1}词法封闭类型声明中的
.
。super
解决方法是在词法封闭类型声明中引入
class Subclass3 implements Superinterface { void foo() { throw new UnsupportedOperationException(); } Runnable tweak = new Runnable() { void run() { Subclass3.Superinterface.super.foo(); // Illegal } }; }
方法,该方法执行接口private
调用。
答案 2 :(得分:1)
我不认为这是可能的。
这一个:
B b = () -> A.super.doA();
或者这个:
A.super.doA();
是有效的,因为这些语句使用A
实例方法作为上下文。
从匿名类中,由于您无权访问A
实例上下文,因此情况有所不同。
因此无法引用A
匿名类中的方法可以引用封闭方法的final
变量或封闭方法的实例(通过前缀classname.this
),但该方法的行为不能像在{的上下文中执行一样{1}}实例方法:A
的含义。
我认为JLS的一部分必须指明这一点。