標籤:java 內部類
java的內部類、匿名類本來以為自己用的已經很溜了, 結果, 就在昨天晚上12點來鐘發生了重大事故。要說事故的嚴重性呢,那就是導致我一晚上沒有睡著覺。
那下面先用一段類比代碼來描述下我出現的問題的:
public class Test {public static void main(String[] args) throws InterruptedException {View v = new View();v.show(1);Thread.sleep(500);v.mTextView.execute();Thread.sleep(1000);v.show(2);Thread.sleep(500);v.mTextView.execute();}}class View {public TextView mTextView;public void show(int position) {if(mTextView == null) {mTextView = new TextView();mTextView.setListener(new Listener() {@Overridepublic void click() {System.out.println("position = " + position);}});}mTextView.show();}}class TextView {private Listener mListener;public void setListener(Listener l) {mListener = l;}public void execute() {mListener.click();}public void show() {System.out.println("textview show...");}}interface Listener {public void click();}
不出意外的話, console肯定是列印的1 2, 但是偏偏就在這困擾到我了,列印的結果是1 1, 仔細順一下代碼,我們就應該去思考這個匿名內部類到底是怎麼使用的外部類那個方法的參數的。
從列印的結果上看, 我猜想肯定是在這個內部類的執行個體中儲存了position參數,那帶著這個猜想,我們來debug一下程式。
這是第一次執行到的時候, 發現什麼問題了嗎。 在mListener中竟然有一個和position相關的變數。到這裡,我們感覺那個猜測可能是正確的。再往下思考,既然在mListener對象中儲存了這個變數,那麼下次執行到,同一個對象,所以變數肯定是相同的,這樣也就解開我們的疑惑了。
總結一下:
在我們new一個匿名內部類的時候,如果使用了方法中的東西,那麼jvm會給我們的匿名類加一個構造方法,並且將這個參數傳遞進來,例如上面的例子中:
class View$Listener {public View$Listener(int position) {this.Listener$position = position;}public void click() {...}}
既然知道了這些,那麼我們的疑惑也就解開了, 那上面的問題怎麼解決呢? 其實我們很早之前就已經知道解決方案了,想想android中自訂Adapter的getView()方法,你會恍然大悟。上面的問題怎麼解決呢?就是把setListener放到if後面,而不是裡面。
public class Test {public static void main(String[] args) throws InterruptedException {View v = new View();v.show(1);Thread.sleep(500);v.mTextView.execute();Thread.sleep(1000);v.show(2);Thread.sleep(500);v.mTextView.execute();}}class View {public TextView mTextView;public void show(int position) {if(mTextView == null) {mTextView = new TextView();//mTextView.setListener(new Listener() {//@Override//public void click() {//System.out.println("position = " + position);//}//});}mTextView.setListener(new Listener() {@Overridepublic void click() {System.out.println("position = " + position);}});mTextView.show();}}class TextView {private Listener mListener;public void setListener(Listener l) {mListener = l;}public void execute() {mListener.click();}public void show() {System.out.println("textview show...");}}interface Listener {public void click();}
這樣在每次調用show方法時,都會重新new一個Listener內部類。
從一次意外開始說java匿名內部類