1. 內部類里面使用外部類的局部變量時,其實就是內部類的對象在使用它,內部類對象生命周期中都可能調用它,而內部類試圖訪問外部方法中的局部變量時,外部方法的局部變量很可能已經不存在了,那么就得延續其生命,拷貝到內部類中,而拷貝會帶來不一致性,從而需要使用final聲明保證一致性。說白了,內部類會自動拷貝外部變量的引用,為了避免:1. 外部方法修改引用,而導致內部類得到的引用值不一致 2.內部類修改引用,而導致外部方法的參數值在修改前和修改后不一致。於是就用 final 來讓該引用不可改變。
2. 內部類通常都含有回調,引用那個匿名內部類的函數執行完了就沒了,所以內部類中引用外面的局部變量需要是final的,這樣在回調的時候才能找到那個變量,而如果是外圍類的成員變量就不需要是final的,因為內部類本身都會含有一個外圍了的引用(外圍類.this),所以回調的時候一定可以訪問到。例如:
private Animator createAnimatorView(final View view, final int position) { MyAnimator animator = new MyAnimator(); animator.addListener(new AnimatorListener() { @Override public void onAnimationEnd(Animator arg0) { Log.d(TAG, "position=" + position); } }); return animator; }
內部類回調里訪問position的時候createAnimatorView()早就執行完了,position如果不是final的,回調的時候肯定就無法拿到它的值了,因為局部變量在函數執行完了以后就被回收了。
3. 我們反編譯看一下,首先定義接口和匿名內部類:
public interface MyInterface { void doSomething(); } public class TryUsingAnonymousClass { public void useMyInterface() { final Integer number = 123; System.out.println(number); MyInterface myInterface = new MyInterface() { @Override public void doSomething() { System.out.println(number); } }; myInterface.doSomething(); System.out.println(number); } }
我們進行反編譯,結果是:
class TryUsingAnonymousClass$1 implements MyInterface { private final TryUsingAnonymousClass this$0; private final Integer paramInteger; TryUsingAnonymousClass$1(TryUsingAnonymousClass this$0, Integer paramInteger) { this.this$0 = this$0; this.paramInteger = paramInteger; } public void doSomething() { System.out.println(this.paramInteger); } }
可以看到名為number的局部變量是作為構造方法的參數傳入匿名內部類的。
如果Java允許匿名內部類訪問非final的局部變量的話,那我們就可以在TryUsingAnonymousClass$1中修改paramInteger,但是這不會對number的值有影響,因為它們是不同的reference。
這就會造成數據不同步的問題。
所以,Java為了避免數據不同步的問題,做出了匿名內部類只可以訪問final的局部變量的限制。
參考:http://cuipengfei.me/blog/2013/06/22/why-does-it-have-to-be-final/