2015-06-11 65 views
11

的匿名類訪問非final類成員我們知道,只有最後的局部變量可以在匿名類訪問,並有一個很好的理由在這裏:Why are only final variables accessible in anonymous class?爲什麼封閉類

然而,我發現,一個匿名類仍然可以訪問非final的變量,如果變量是封閉類的成員字段:How can I access enclosing class instance variables from inside the anonymous class?

我很困惑。我們確保只有最終的局部變量可以在匿名類中訪問,因爲我們不希望變量在匿名類和本地函數之間不同步。如果我們嘗試訪問匿名類中的非最終封閉類成員,則同樣的原因應適用於該案例。

爲什麼會不會成爲問題?

+0

'如果我們嘗試訪問匿名類中的非最終實例變量,同樣的原因可能適用於這種情況。「....這就是所謂的訪問對象的字段,從其引用。爲什麼對象的起源或其類的聲明很重要? –

+0

@ user2499800:你見過我的回答嗎?你在哪裏與此達成?如果您回覆,請稍等... :) –

回答

4

在局部變量的情況下,該變量的副本是匿名類實例接收的內容。由於這個原因,局部變量必須在final之前在匿名類中使用,以便它的值可能在以後不會改變。

在封閉類的成員字段的情況下,沒有副本。相反,匿名類獲取對封閉類的引用,從而訪問外部類的任何/所有成員字段和方法。所以,即使字段的值發生變化,該變化也會反映在匿名類中,因爲它是相同的參考。

我很困惑。我們確保只有一個最終的局部變量可以 在匿名類訪問,因爲我們不希望出現這種情況的變量 應該出不同步匿名類和本地函數之間。 同樣的原因應該適用於這種情況,如果我們嘗試訪問包含匿名類中的類成員的非最終 。

正如你看到的,事實並非如此。複製僅適用於局部變量,不適用於封閉類的成員字段。 原因當然是,匿名類擁有對封閉類的隱式引用,並且通過該引用,它可以訪問外部類的任何/所有成員字段方法&。

引述下面的鏈接:

成員變量存在包圍對象的壽命期間,因此它可以由內部類的實例被引用。然而,局部變量僅在方法調用期間存在,並且由編譯器進行不同處理,因爲其隱式副本作爲內部類的成員生成。沒有聲明局部變量fi​​nal,可以改變它,導致由於內部類仍然指向該變量的原始值而引起的細微錯誤。

參考文獻:

Why a non-final 「local」 variable cannot be used inside an inner class, and instead a non-final field of the enclosing class can?

+0

您可以通過解釋爲什麼可以使用參考而不是副本來改善您的答案。雖然它可能很明顯。 – theMfromA

+0

右鍵,完成! 「 –

+0

」相反,匿名類實例獲取對實際成員字段的引用。「不。他們只有一個對封閉實例的引用。 – newacct

2

非靜態/內部類具有包圍實例的提述。所以他們可以隱式引用實例變量和方法。

如果它是一個Parameter,即使封閉類也不知道它的任何內容,因爲它只能從已定義此變量的Method訪問。

Like Y.S.已經指出:

在一個局部變量的情況下,變量的副本是什麼匿名類實例都有

+0

「非靜態內部類」是一個重言式。如果類是靜態的並在另一個類中聲明,它們被稱爲「靜態成員類」。如果它們不是靜態的,它們被稱爲「內部類」。 –

+0

@ErwinBolwidt:是的,我忘了斜槓。我的意思是寫非靜態/內部類。謝謝你的提示。 –

1

更反過來,一個局部變量,說x只要方法調用,匿名實例的外部就會生效。其對象引用存儲在調用堆棧中;包含對象引用的堆棧上的x ==地址。在匿名實例中的x拷貝,因爲它的生存期不同,更長或更短。

由於現在有兩個變量,決定不允許分配給x(奇怪地實施)並要求該變量爲「有效最終」。

訪問外部成員,是因爲匿名實例的內部類也包含OuterClass.this引用。

1

考慮下面的例子

class InnerSuper{ 
    void mInner(){} 
} 
class Outer{ 
    int aOuter=10; 
    InnerSuper mOuter(){ 
     int aLocal=3999; 
     class Inner extends InnerSuper{ 
      int aInner=20; 
      void mInner(){ 
       System.out.println("a Inner : "+aInner); 
       System.out.println("a local : "+aLocal); 
      } 
     } 
     Inner iob=new Inner(); 
     return iob; 
    } 
} 

class Demo{ 
    public static void main(String args[]){ 
     Outer ob=new Outer(); 
     InnerSuper iob=ob.mOuter(); 
     iob.mInner(); 
    } 
} 

這不會生成Java 1.8或以上的任何錯誤。但在以前的版本中,這會生成一個錯誤,要求您明確聲明在內部類中訪問的本地變量爲final。 因爲編譯器會做的是保留由內部類訪問的本地變量的副本,以便即使方法/塊結束並且局部變量超出作用域,副本也將存在。它要求我們聲明final,因爲如果變量在程序後面動態更改其值後在本地內部類聲明瞭匿名類,則由編譯器創建的副本不會更改爲其新值並且可能通過不產生預期產出而導致內部階層出現問題。因此它建議我們明確宣佈它是最終的。

但在Java 1.8,因爲編譯器聲明訪問的本地變量隱含最終也不會產生錯誤。 它在Java文檔

匿名類遵循未聲明爲final或者有效最終其封閉 範圍無法訪問局部變量陳述。

讓我來解釋一下有效的final。考慮以上程序

class Outer{ 
    int aOuter=10; 
    InnerSuper mOuter(){ 
     int aLocal=3999; 
     class Inner extends InnerSuper{ 
      int aInner=20; 
      void mInner(){ 
       System.out.println("a Inner : "+aInner); 
       System.out.println("a local : "+aLocal); 
      } 
     } 
     aLocal=4000; 
     Inner iob=new Inner(); 
     return iob; 
    } 
} 

即使在Java中1.8,這將產生一個錯誤的以下修改過的版本。這是因爲aLocal是在程序中動態分配的。這意味着變量不能被編譯器認爲是有效的最終。從我所瞭解的情況來看,編譯器聲明瞭最終沒有動態改變的變量。這被稱爲變量是有效的最終

因此,建議您聲明本地內部類或匿名類顯式最終訪問的局部變量以避免任何錯誤。