从一次意外开始说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内部类。


  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

亓斌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值