對(duì)于Java語言中的final使用趣惠,大家應(yīng)該很熟悉狸棍,可以修飾類,表示不可繼承味悄;可以修飾方法草戈,表示不可被子類重寫;可以修飾變量侍瑟,表示不可以被二次賦值唐片。那么,Java匿名內(nèi)部類訪問外部變量涨颜,為何需被標(biāo)志為final费韭?這跟上述三個(gè)特性有關(guān)系嗎?
一庭瑰、問題的提出
Java編程中星持,使用匿名內(nèi)部類訪問外部方法的局部變量是一件很常見的事件,比如以下代碼弹灭,使用匿名內(nèi)部類設(shè)置控件的監(jiān)聽器是再常見不過了督暂,下面的例子中羹令,因?yàn)槟涿O(jiān)聽器類訪問了外部局部變量name,編譯器提示name變量必須使用final修飾损痰。
//初始化按鈕的監(jiān)聽器
public void initListener(Button btn ){
final int name = "王大錘"; //必須標(biāo)記為final
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
btn.setText(name); //動(dòng)態(tài)改變按鈕的文字
}
});
}
為何name變量需被標(biāo)志為final福侈?這深層次的原因是什么?為什么有這樣一個(gè)讓人摸不著頭腦的規(guī)定卢未?
二肪凛、解釋
這要從閉包說起,匿名內(nèi)部類和外部方法形成了一個(gè)閉包辽社,因此伟墙,匿名內(nèi)部類能夠訪問外部方法的變量,看起來是一種“天經(jīng)地義”的事情滴铅,Java語言當(dāng)然也需要實(shí)現(xiàn)這種特性戳葵,但是這里遇到了一個(gè)問題。
匿名內(nèi)部類的生命周期可能比外部的類要長汉匙,因此訪問外部局部變量有可能是訪問不到的拱烁。
那怎么辦呢?Java語言為了實(shí)現(xiàn)這種特性噩翠, 只好將外部的局部變量偷偷的賦值了一份給匿名內(nèi)部類戏自。那這樣匿名內(nèi)部類就可以肆無忌憚的訪問外部局部變量了。
問題又來了伤锚,這種通過賦值的形式有一個(gè)缺陷擅笔,匿名內(nèi)部類不可以修改“原來的局部變量”,因?yàn)槭且环荨皬?fù)制品”屯援,修改復(fù)制品對(duì)原變量沒什么影響啊猛们。
那怎么辦? Java語言干脆強(qiáng)制要求被匿名內(nèi)部類訪問的外部局部變量必須是final的狞洋,什么意思呢弯淘?就是“一刀切”,不讓修改了徘铝。