簡體   English   中英

為什么method()和super.method()引用匿名子類中的不同內容?

[英]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

同樣, TSolution ,第一個詞法封閉類型,因為聲明立即包含方法調用的類是匿名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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM