匿名內部類是什么
首先, 如果在一個A類里面定義一個B類, 那么B類就是內部類, A類是外部類. 內部類就相當于外部類的一個成員, 你可以把內部類看成一個整體. 它又分為靜態內部類和非靜態內部類.
匿名內部類是非靜態內部類的一種特殊情況, 匿名即沒有類名, 因此就不可能有構造函數, 不能創建對象.
但請注意實質上匿名內部類是有類名和構造函數的. 由編譯器創建.
為什么會有匿名內部類
為了程序員的方便以及代碼的簡潔.本來程序員應該先創建一個類繼承抽象類或實現接口再創建對象, 但很多情況下, 這個類只會被使用一次, 似乎單獨存在的必要性不大.. 為了簡便, 匿名內部類允許我們在主方法當中進行抽象類/接口的實例化, 同時也可以進行對象的創建. 比如移動端開發最常見的 View.onClickListener
這個接口:
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TO DO
}
}
直接通過匿名內部類的方式, 在主方法中實現接口創建一個匿名對象傳遞給按鈕. 除了簡潔以外, 還有其他的作用, 在下文單獨講解作用.
定義
匿名內部類的兩種定義方式:
new 實現接口()
{
// 匿名內部類類體部分
}
new 父類構造器(實參列表)
{
// 匿名內部類類體部分
}
這兩種方式的定義分別對應兩種方式,一種是接口,另一種是父類構造器.
對于實現接口, 由于接口是沒有構造函數的, 注意這里一定是空參數.
第二種是調用父類的構造器, 注意此處可以是空參數, 也可以傳入參數.
作用
匿名內部類除了代碼簡潔以外, 主要有以下幾個用途:
- 覆蓋父類的方法
- 實現接口的方法
- 使用匿名內部類傳入代碼塊進行初始化
覆蓋父類的方法
覆蓋父類的 run()
方法:
new Thread() {
@Override
public void run() {
// super.run();
// TO DO
}
}
實現接口的方法
即前面提到的 View.OnClickListener
接口:
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TO DO
}
}
使用匿名內部類傳入代碼塊進行初始化
假設我們有代碼:
List<String> friends = new ArrayList<>();
friends.add("Harry");
friends.add("Tony");
invite(friends);
借助雙括號初始化 (double brace initialization) 技巧, 若這個 friends
數組之后不會再使用的話, 我們可以把它構造成一個匿名列表:
invite(new ArrayList<String>() {
{
add("Harry");
add("Tony");
}
});
匿名內部類的名字
特殊的一點是, 匿名, 當然是沒有名字, 但其實匿名內部類是有名字的. 只是不是人類認知意義上的名字, 無法在其他地方使用:
Foo foo = new Foo() {
@Override
int bar() {
return 0;
}
}
這個內部類, 在字節碼文件 .class
中也會被定義出來.
class package.name.outerClassName$1.
內部類的命名方式為外部類名 + $ + N, N 是匿名內部類的順序. 從 1 開始.
注意事項
建立一個與超類類似, 但不完全相同的匿名子類非常容易, 但這樣的子類對象在使用 equals
方法時要特別當心, 我們在定義 equals
時, 一般要對類型進行測試:
if(getClass() != other.getClass()) return false;
這個測試條件在用于匿名子類時會失效!