Yur*_*val 15 java inheritance inner-classes
我正在解决一些练习,以更好地理解java中的内部类是如何工作的.我发现了一个非常有趣的运动.练习的条件printName()
是以最小的变化打印"sout"而不是"main".有它的代码:
public class Solution {
private String name;
Solution(String name) {
this.name = name;
}
private String getName() {
return name;
}
private void sout() {
new Solution("sout") {
void printName() {
System.out.println(getName());
// the line above is an equivalent to:
// System.out.println(Solution.this.getName);
}
}.printName();
}
public static void main(String[] args) {
new Solution("main").sout();
}
}
Run Code Online (Sandbox Code Playgroud)
我们有一个有趣的情况 - 这两个类有-A和有-A连接.这意味着匿名内部类扩展了外部类,并且内部类的对象也引用了外部类的对象.如果您运行上面的代码,将打印"main".子进程无法getName()
通过继承来调用父进程.但是作为内部类的子类使用对父(外部类)的引用来访问该方法.
解决这一任务的最简单方法是访问修饰符改变getName()
从private
其他任何事情.因此,孩子能够getName()
通过继承使用,并且由于后期绑定"sout"将被打印.
解决此任务的另一种方法是使用super.getName()
.
private void sout() {
new Solution("sout") {
void printName() {
System.out.println(super.getName());
}
}.printName();
}
Run Code Online (Sandbox Code Playgroud)
我无法理解它是如何工作的.有人可以帮我理解这个问题吗?
谢谢你的尝试)
Sot*_*lis 10
Java语言规范(JLS)在编译器解析方法调用表达式的上下文中指出
如果表单是
super . [TypeArguments] Identifier
,则要搜索的类是其声明包含方法调用的类的超类.
类声明中包含的方法调用,在这种情况下,是匿名的Solution
子类,其超类是Solution
.在确定将使用哪个实例来调用该方法的上下文中, JLS 继续说
如果表单是
super . [TypeArguments] Identifier
,则目标引用是值this
.
this
在这种情况下,指的是匿名Solution
子类的实例.该实例的name
字段初始化为值"sout"
,因此getName()
返回.
在原始样本中,
new Solution("sout") {
void printName() {
System.out.println(getName());
}
}.printName();
Run Code Online (Sandbox Code Playgroud)
所述getName()
方法调用是不合格的,并且因此不同的规则适用.那是
如果有一个封闭类型声明,该方法是一个成员,
T
那么就是最里面的类型声明.要搜索的类或接口是T
.
T
这里是Solution
类,因为它是匿名Solution
子类的最内层封闭类型,并且getName()
是它的成员.
然后,JLS声明
否则,让我们
T
成为该方法所属的封闭类型声明,并n
设为一个整数,这T
是一个类的第n个词法封闭类型声明,其声明立即包含方法调用.目标参考是第n个词汇封闭的实例this
.
同样,T
是Solution
第一个词法封闭类型,因为声明立即包含方法调用的Solution
类是匿名子类.this
是匿名Solution
子类实例.因此,目标参考是第一个词汇封闭的实例this
,即.的Solution
情况下,其name
场与值初始化"main"
.这就是原始代码打印的原因"main"
.
这种行为可能看似违反直觉,但通过一些重构就变得清晰了.
因此,该sout()
方法实际上可以重写为
private void sout() {
new Solution("sout") {
void printName() {
String name = getName();
System.out.println(name);
}
}.printName();
}
public static void main(String[] args) {
Solution mainSolution = new Solution("main");
mainSolution.sout();
}
Run Code Online (Sandbox Code Playgroud)
调用对象的sout()
方法mainSolution
,创建一个子Solution
对象,该对象具有一个printName()
调用的附加方法
getName();
它只在父mainSolution
对象中声明.
如果getName()
声明为私有,则它不会被覆盖,但它仍然可以从内部类访问,因此getName()
引用的名称mainSolution
,即to main
.
如果getName()
没有修饰符,或者声明为protected或public,那么它将被继承(覆盖)并引用子Solution
对象的名称,即to sout
,因此将打印"sout".
通过替换getName()
在sout()
用
Solution.this.getName()
Run Code Online (Sandbox Code Playgroud)
字符串"main"将在两个场景中打印.
用它中的任何一个替换它
this.getName()
super.getName()
Run Code Online (Sandbox Code Playgroud)
如果该getName()
方法被声明为private
编译错误将发生,否则将打印字符串"sout".
归档时间: |
|
查看次数: |
284 次 |
最近记录: |