从一开始,说出事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() {
				@Override
				public 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() {
			@Override
			public 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内部类。


版权声明:本文来源于Loader's Blog。博客,未经同意不得转载。

原文地址:https://www.cnblogs.com/blfshiye/p/4826924.html