Android开发中,匿名类的应用使得开发更加灵活,而匿名类的滥用却让程序变得杂乱无章、难以维护。程序员都是喜欢偷懒的,我们常常看到一个Activity类中布满了匿名类,有Thread、有Handler、有Adapter、有各种Listener以及给匿名类传递数据的final变量,给人的感觉就像随地大小便,很不文明。
Java毕竟不是Javascript,没有闭包和没有匿名函数,把面向对象设计的语言用函数式编程的思想来写是很牵强的。下面贴一段JS代码:
MongoClient.connect('mongodb://127.0.0.1:27017/test', function(err, db) {if(err) throwerr;
db.dropDatabase(function(err, done) {
db.createCollection('test_custom_key', function(err, collection) {
collection.insert({'a':1}, function(err, docs) {
collection.find({'_id':new ObjectID("aaaaaaaaaaaa")}).toArray(function(err, items) {
console.dir(items);//Let's close the db
db.close();
});
});
});
});
});
得益于JS的闭包、匿名函数、函数即变量等特性,JS开发者可以将函数嵌套发挥到令人叹为观止的地步,而且即使套了很多层,也不会显得很臃肿。你能想想如果Java套这么多层会是什么效果吗?或许编辑器都显示不下最长的缩进了,但能多Android开发者就好这口,来看个例子:
public class MyActivity extendsActivity {
@Overrideprotected voidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
((ListView) findViewById(R.id.listview)).setAdapter(newBaseAdapter() {
@Overridepublic View getView(intarg0, View arg1, ViewGroup arg2) {if (arg1 == null) {
arg1= LayoutInflater.from(MyActivity.this).inflate(
R.id.feed, arg2,false);
}
arg1.findViewById(R.id.subject).setOnClickListener(newOnClickListener() {
@Overridepublic voidonClick(View v) {final Handler handler = newHandler(newCallback() {
@Overridepublic booleanhandleMessage(
Message msg) {//此处省略50行
return false;
}
});new Thread(newRunnable() {
@Overridepublic voidrun() {//此处省略100行
handler.sendEmptyMessage(0);
}
}).start();
}
});returnarg1;
}//。。。。。。
});
}
}
这是个真实的例子,其中很多习惯很多安卓开发者也有。这个onCreate函数竟然有近千行代码,已经完全丧失可读性了。如果你走了有人来接替你的活儿,看到这样的代码他一定会把你骂死。
下面我就Android开发中常见的匿名类给出自己的替代方案,仅供参考,错误之处还请指证。
用继承接口代替匿名类
这种方式适合各种Listener,这里以OnClickListener为例作说明。绑定事件一般是在onCreate时完成的,所以在onCreate中可能会有很多个OnClickListener的匿名类,比如:
findViewById(R.id.button).setOnClickListener(newOnClickListener() {
@Overridepublic voidonClick(View arg0) {//....
}
});
这时onCreate方法就会变得冗长,而且包含很多不应该出现在onCreate中的处理逻辑,如果我们让Activity去实现OnClickListener,将所有点击处理的listener都设为这个activity,在onClick中再根据view.getId()来分支处理,程序的结构就会紧凑很多:
public class DummyActivity extends Activity implementsOnClickListener {//.....
@Overrideprotected voidonCreate(Bundle savedInstanceState) {for(int id : in new int[] { R.id.button }) {
findViewById(id).setOnClickListener(this);
}
}
@Overridepublic voidonClick(View arg0) {switch(arg0.getId()) {caseR.id.button://......
break;
}
}
}
包括onLongClickListener等针对单一View的Listener都可以通过让Activity继承接口的方式来操作。如果是针对列表项的ItemListener,建议让Adapter去继承接口。
用子类代替匿名类
如果一个Activity中有AdapterView的子类,如ListView、GridView,就会设置相应的Adapter,除了最简单的情形可以用SimpleAdapter、ArrayAdapter直接映射,很多情况都都需要继承BaseAdapter之类后重写一些方法,逻辑比较简单时用内部类不失为可取办法。例如很多人喜欢在onCreate中这么写:
((ListView)findViewById(R.id.listView)).setAdapter(newBaseAdapter() {
@Overridepublic View getView(intarg0, View arg1, ViewGroup arg2) {//......
}//......
});
为了不在onCreate参杂不相关的逻辑,保持代码结构合理,你可以尝试这样处理:
public class DummyActivity extendsActivity {//.....
@Overrideprotected voidonCreate(Bundle savedInstanceState) {
((ListView)findViewById(R.id.listView)).setAdapter(newMyAdapter(....));
}private static class MyAdapter extendsBase Adapter{
MyAdapter(...) {
.......
}
@Overridepublic View getView(intarg0, View arg1, ViewGroup arg2) {
.........
}
}
}
至于static与否则取决于你是否要用到动态成员变量了,这种方式使得参数传递也更加方便:只需通过构造函数传入,不必通过final关键字。
用外部类代替匿名类
对于Adapter这类逻辑简单的类可以用内部类处理,如果涉及到更加复杂的逻辑,比如数据处理、I/O、数据库操作等,建议用外部类使各个类的业务属性尽可能单一。比如Thread类,跟Activity类相关度本来就很低,为了偷懒非得硬生生塞进Activity中显然是不合适的,无论是匿名类还是子类的形式,这里就不举具体例子了。至于外部类的命名,如果想不到合适的名字,可考虑前缀命名,比如现在有一个DummyActivity,之前在里面塞了一个MyThread,那提出来再叫MyThread显然不合适,就以前缀命名的方式改为DummyActivityThread就一目了然了。
小结
Javascript是门很优秀的语言,拥有函数式编程的诸多特性,合理的灵活嵌套可以使代码紧凑而优雅,而Java是一门严格面向对象的语言,匿名类嵌套的滥用会让你的程序臃肿混乱,建议不要把Java写成了Javascript的风格,那样你会很难维护。