[英]Why do method() and super.method() refer to different things in an anonymous subclass?
我正在解決一些練習,以更好地理解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();
}
}
我們有一個有趣的情況 - 這兩個類有-A和有-A連接。 這意味着匿名內部類擴展了外部類,並且內部類的對象也引用了外部類的對象。 如果您運行上面的代碼,將打印“main”。 子getName()
無法通過繼承調用父getName()
。 但是作為內部類的子類使用對父(外部類)的引用來訪問該方法。
解決此任務的最簡單方法是將getName()
的訪問修飾符從private
更改為其他任何內容。 因此,孩子能夠通過繼承使用getName()
,並且由於后期綁定“sout”將被打印。
解決此任務的另一種方法是使用super.getName()
。
private void sout() {
new Solution("sout") {
void printName() {
System.out.println(super.getName());
}
}.printName();
}
我無法理解它是如何工作的。 有人可以幫我理解這個問題嗎?
謝謝你的嘗試)
Java語言規范(JLS) 在編譯器解析方法調用表達式的上下文中指出
如果表格是
super . [TypeArguments] Identifier
super . [TypeArguments] Identifier
,然后要搜索的類是其聲明包含方法調用的類的超類。
在這種情況下, 其聲明包含方法調用的類是匿名Solution
子類,其超類是Solution
。 在確定將使用哪個實例來調用該方法的上下文中, JLS繼續說
如果表格是
super . [TypeArguments] Identifier
super . [TypeArguments] Identifier
,然后目標引用是this
的值。
this
,在這種情況下,是指在匿名的情況下Solution
的子類。 該實例的name
字段初始化為值"sout"
,因此getName()
返回。
在原始樣本中,
new Solution("sout") {
void printName() {
System.out.println(getName());
}
}.printName();
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();
}
調用mainSolution
對象的sout()
方法,創建一個具有附加printName()
方法的子Solution
方法,該方法調用
getName();
它只在父mainSolution
對象中聲明。
如果將getName()
聲明為private,則不會覆蓋它,但仍然可以從內部類訪問它,因此getName()
引用mainSolution
的名稱,即main
。
如果getName()
沒有修飾符,或者聲明為protected或public,那么它將被繼承(覆蓋)並引用子Solution
對象的名稱,即sout
,因此將打印“sout”。
通過用sout()
替換sout()
getName()
Solution.this.getName()
字符串“main”將在兩個場景中打印。
用它中的任何一個替換它
this.getName()
super.getName()
如果將getName()
方法聲明為private
則會發生編譯錯誤,否則將打印字符串“sout”。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.