从匿名内部调用覆盖的默认方法 Class
Calling Overridden Default Method from Anonymous Inner Class
考虑这段代码:
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
中。查看注释掉的代码。
有什么想法吗?
我不认为这是可能的。
这个:
B b = () -> A.super.doA();
或这个:
A.super.doA();
是有效的,因为这些语句使用 A
实例方法作为上下文。
与匿名 class 相比,情况有所不同,因为您无权访问 A
实例上下文。
所以A
无法引用
匿名 class 中的方法可以引用封闭方法的 final
变量或封闭方法的实例(通过前缀 classname.this
),但该方法不能像在A
实例方法的上下文:A.super.doA()
的含义。
我认为JLS的一段必须说明这一点。
lambda 和匿名 classes 中的代码被区别对待
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).
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.
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()). 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 中的代码
The keyword this
may be used in a lambda expression only if it is allowed in the context in which the lambda expression appears. Otherwise, a compile-time error occurs.
我认为它也可以应用于关键字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();
// ...
}
}
此代码段打印
class Test
[interface A]
我们会将其与匿名 class 结果进行比较。
匿名代码 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();
}
}
片段输出
class Test
[interface B]
记住匿名 class 有自己的 this
和 super
并且它不继承 A
(并且不能这样做),它变成明确 A.super.doA();
无法在其上下文中编译。
解决方法
解决方法可能是通过 lambda 记住封闭上下文,并在匿名方法中调用该 lambda class:
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();
}
}
如 中所述,由于 this
和 super
的不同含义(与内部 类 相比),这在 lambda 表达式中是可能的。
无法对内部 类 执行相同操作已在 The Java® Language Specification, §15.12.1 中明确解决:
The 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.
…
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.
…
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 .
super
in the lexically enclosing type declaration.
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.
考虑这段代码:
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
中。查看注释掉的代码。
有什么想法吗?
我不认为这是可能的。
这个:
B b = () -> A.super.doA();
或这个:
A.super.doA();
是有效的,因为这些语句使用 A
实例方法作为上下文。
与匿名 class 相比,情况有所不同,因为您无权访问 A
实例上下文。
所以A
无法引用
匿名 class 中的方法可以引用封闭方法的 final
变量或封闭方法的实例(通过前缀 classname.this
),但该方法不能像在A
实例方法的上下文:A.super.doA()
的含义。
我认为JLS的一段必须说明这一点。
lambda 和匿名 classes 中的代码被区别对待
Unlike code appearing in anonymous class declarations, the meaning of names and the
this
andsuper
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).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.
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()). If it is necessary for a lambda expression to refer to itself (as if viathis
), a method reference or an anonymous inner class should be used instead.
lambda 中的代码
The keyword
this
may be used in a lambda expression only if it is allowed in the context in which the lambda expression appears. Otherwise, a compile-time error occurs.
我认为它也可以应用于关键字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();
// ...
}
}
此代码段打印
class Test
[interface A]
我们会将其与匿名 class 结果进行比较。
匿名代码 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();
}
}
片段输出
class Test
[interface B]
记住匿名 class 有自己的 this
和 super
并且它不继承 A
(并且不能这样做),它变成明确 A.super.doA();
无法在其上下文中编译。
解决方法
解决方法可能是通过 lambda 记住封闭上下文,并在匿名方法中调用该 lambda class:
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();
}
}
如 this
和 super
的不同含义(与内部 类 相比),这在 lambda 表达式中是可能的。
无法对内部 类 执行相同操作已在 The Java® Language Specification, §15.12.1 中明确解决:
The 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 unqualifiedsuper
in the lexically enclosing type declaration.…
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.
…
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
.
super
in the lexically enclosing type declaration.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 interfacesuper
call.