淺談Java的匿名類


在實際的項目中看到一個很奇怪的現象,Java可以直接new一個接口,然后在new里面粗暴的加入實現代碼。就像下面這樣。那么問題來了,new出來的對象沒有實際的類作為載體,這不是很奇怪嗎?

思考以下代碼的輸出是什么?

Runnable x = new Runnable() { @Override public void run() { System.out.println(this.getClass()); } }; x.run();

實際答案是出現xxxx$1這樣一個類名,它是編譯器給定的名稱。

匿名類

匿名類相當於在定義類的同時再新建這個類的實例。我們來看看匿名類的編譯結果。

這個類的代碼如下:

public class Test { public void test() { Runnable r = new Runnable(){ @Override public void run(){ System.out.println("hello"); } }; } }

來看看它的編譯結果,通過javap反向編譯Test.class,得到的結果如下:

SourceFile: "Test.java" EnclosingMethod: #20.#21 // Test.test InnerClasses: #6; //class Test$1

發現了一個字段叫EnclosingMethod,說明這個類是定義在Test.test方法下的。那現在有個問題,如果有兩個test方法,會出現什么呢?

原來是旁邊這個注釋不太准確,實際上會包含函數簽名的。請看Constant Pool部分,這里的#21指向了這個函數簽名。

#21 = NameAndType #34:#16 // test:()V

匿名類的語法

這里舉一個簡單的例子:

Runnable hello = new Runnable() { public void run() { System.out.println("hello"); } };

一個匿名類由以下幾個部分組成:

  1. new操作符
  2. Runnable:接口名稱。這里還可以填寫抽象類、普通類的名稱。
  3. ():這個括號表示構造函數的參數列表。由於Runnable是一個接口,沒有構造函數,所以這里填一個空的括號表示沒有參數。
  4. {...}:大括號中間的代碼表示這個類內部的一些結構。在這里可以定義變量名稱、方法。跟普通的類一樣。

訪問權限

那么匿名內部類能訪問哪些東西呢?按照規則,可以訪問如下內容:

  1. 訪問外層Class里面的字段。
  2. 不能訪問外層方法中的本地變量。除非變量是final。
  3. 如果內部類的名稱和外面能訪問的名稱相同,則會把名稱覆蓋掉。
public class A { private int foo; public void test() { Runnable r = new Runnable() { System.out.println(foo); }; } }

匿名類里面不可以有的東西:
1.不能定義靜態初始化代碼塊(Static Initializer)。比如下面的代碼是不符合語法的:

public class A { public void test() { Runnable r = new Runnable() { static { System.out.println("hello"); } }; } }

2.不能在匿名類里面定義接口。

比如:

public class A { public void test() { Runnable r = new Runnable() { public interface Hello { }; }; } }

和上面一樣,也是為了語義的清晰。interface只能定義靜態的。

3.不能在匿名類中定義構造函數。

public class A { public void test() { Runnable r = new Runnable() { public Runnable() { } }; } }

因為匿名類沒有名字,而構造函數需要把類名作為方法名才能看成構造函數。
匿名類中可以包含的東西有:

  1. 字段
  2. 方法
  3. 實例初始化代碼
  4. 本地類

為什么不能定義靜態初始化代碼

事實上,內部類中不能定義任何靜態的東西。

關鍵字:Inner class cannot have static declarations

參考資料:http://stackoverflow.com/questions/975134/why-cant-we-have-static-method-in-a-non-static-inner-class

StackOverFlow上看起來有一種解釋如下。

首先來看一個內部類。

public class A { public class B { } }

它編譯之后,會變成下面這種含義:

public class A { public static class B { private final A parent; public B(A parent) { this.parent = parent; } } }

所以,按照這么說,內部類就是一種語法糖。當我們定義靜態變量時,就會產生下面這種歧義。下面的代碼看起來沒什么問題。

public class A { private int a; public class B { public static void test() { a = 1; } } }

但是編譯之后,問題就來了。

public class A { private int a; public static class B { private final A parent; public B (A parent) { this.parent = parent; } public static void test() { parent.a = 1; // 這里有語法錯誤 } } }

所以,歸根結底,Java為了保持清晰的語法,不允許這種有歧義的語法存在。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM