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