锁定老帖子 主题:对Java中匿名类的一些思考
该帖已经被评为良好帖
|
|
---|---|
作者 | 正文 |
发表时间:2009-12-02
最后修改:2009-12-03
引言Java中的匿名类是比较有意思的一种编程方式,在swing中关于监听器的注册时,经常可见到这样的代码: iexit.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { int y = JOptionPane.showConfirmDialog( null, "Confirm exit", "Confirm Exit Dialog", JOptionPane.YES_NO_OPTION); if(y == JOptionPane.YES_OPTION){System.exit(0);} } }); 通常使用匿名类的场景是,临时的创建一个类,实现指定的接口 ,使得调用者可以通过接口的方法来实现一定的操作。比如上边这个例子中,exit这个菜单项,需要一个动作监听器,如果用常规写法,大概如此: class XListener implements ActionListener{ public void actionPerformed(ActionEvent e){ //action here } } XListener l = new XListener(); iexit.addActionListener(l); 虽然完成了同样的工作,但是代码量明显比刚才要多。 实例在javascript,或者其他函数式编程语言中,常常可以见到诸如此类的写法: $("#element").click(function(){ //do something }); $("div.type").each(function(index){ //do something else with the index }); 将一个匿名函数传递给另一个函数,实现一些列定制的动作,正好工作中有个地方要用到类似的东东,就用java的匿名类实现了一套类似的机制: Integer[] is = new Integer[]{ 4,13,65,64,2,4,5,9,10,25,20,32,30 }; //用数组构造一个integer 的 list FPList<Integer> p = new FPList<Integer>(is); p.append(100);//再添加一个元素 //做一次过滤,只有偶数被留下来 FPList<Integer> even = p.filter(new Filter(){ public boolean isLegal(Object item) { int x = ((Integer)item).intValue(); return x % 2 == 0 ? true : false; } }).filter(new Filter(){//再做一次过滤,10的倍数被留下来 public boolean isLegal(Object item){ int x = ((Integer)item).intValue(); return x % 10 == 0 ? true : false; } }); 这样可以将很多的条件AND到一起,对数据做条件过滤。 设计与实现首先,需要定义一个接口(这是匿名类的基础),在这个例子中,我定义了一个过滤器接口,其中只有一个方法,即isLegal(Object item), 这个方法接受一个Obejct参数,返回一个boolean值,调用者根据这个boolean值来对最终结果做过滤: package org.free.fplist; /** * defined what condition should be used in <code>FilterableList.filter()</code> * * @author juntao.qiu@gmail.com * */ public interface Filter { /** * this is a condition definition, pass a object in, and then * a <code>true</code> or <code>false</code> will be returned. * @param item * @return */ boolean isLegal(Object item); } 另外,我们需要一个接口,用来表示一个链表具有被过滤的能力(FPable): package org.free.fplist; /** * This is the interface defined Function-programmable support * * @author juntao.qiu@gmail.com * */ public interface FPable<E> { /** * append a new element to list, and then return <code>this</code> object * * @param e element you want to insert into * @return */ FPable<E> append(E e); /** * do a filter by the given rule, the <code>Filter</code> * object passed in is defined as a interface, and you need * to implement the condition. * * @param f * @return */ FPable<E> filter(Filter f); /** * mapping the action to each item of <code>function-programming-list</code> * and will not affect the original list * * @param act the Action will used to mapping * @return */ FPable<E> mapping(Action act); /** * distinct the <code>FilterableList</code>, keep one same elements only, and * does not affect the List itself. * * @return */ FPable<E> distinct(); /** * for debug only, print the <code>index</code> and <code>content</code> * of each item of a list. */ void print(); } 附加的,我需要对这个链表有函数映射 (map)的支持,上面这个接口中的Action,为另一个接口,同样会被很多的匿名类使用到: package org.free.fplist; public interface Action { public Object doAction(Object item); } 好了,我们现在来看一个FPable的实现FPList,FPList继承了LinkedList,并且实现了FPable,可以对其中的数据进行过滤(前提是传入一个过滤器 ),或者对其中的元素进行映射(传入一个动作 ),FPList会自动的将过滤器和动作作用到List中的每一个元素。
package org.free.fplist; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.Set; public class FPList<E> extends LinkedList<E> implements FPable<E>{ private static final long serialVersionUID = 348375840291148300L; public FPList(){ } /** * construct a fp-list by given array. * * @param es */ public FPList(E[] es){ for(int i = 0;i < es.length;i++){ add(es[i]); } } public FPList<E> filter(Filter f){ FPList<E> filtered = new FPList<E>(); for(int i = 0; i < size();i++){ E o = get(i); if(f.isLegal(o)){ filtered.add(o); } } return filtered; } public FPList<E> append(E e){ add(e); return this; } public FPList<E> distinct(){ FPList<E> filtered = this; Set<E> set = new HashSet<E>(); for(int i = 0; i < filtered.size();i++){ set.add(filtered.get(i)); } filtered.clear(); Iterator<E> it = set.iterator(); while(it.hasNext()){ filtered.add(it.next()); } return filtered; } public FPList<E> mapping(Action act){ FPList<E> mapped = this; for(int i = 0;i < size();i++){ mapped.add(i, (E)act.doAction(get(i))); mapped.remove(i+1); } return mapped; } public void print(){ for(int i = 0;i < size();i++){ System.err.println("index : "+i+", content : "+get(i)); } } } 使用匿名类匿名类的使用是比较方便的,为了代码更简洁,我使用了jQuery中的链机制,其实,大家平时使用的StringBuffer就提供这样的能力。
package org.free.fplist; public class Main { public static void main(String[] args){ String[] as = new String[]{ "Apple", "Borland", "Cisco", "Dell", "Epson", "Flick", "Google" }; FPList<String> k = new FPList<String>(as); k.distinct().filter(new Filter(){ public boolean isLegal(Object item) { return ((String)item).indexOf("e") >= 0 ? true : false; } }).filter(new Filter(){ public boolean isLegal(Object item) { return ((String)item).indexOf("p") >= 0 ? true : false; } }).mapping(new Action(){ public Object doAction(Object item) { return ((String)item)+", co"; } }).print(); Integer[] is = new Integer[]{ 4,13,65,64,2,4,5,9,10,25,20,32,30 }; FPList<Integer> p = new FPList<Integer>(is); p.append(100); FPList<Integer> even = p.filter(new Filter(){ public boolean isLegal(Object item) { int x = ((Integer)item).intValue(); return x % 2 == 0 ? true : false; } }).filter(new Filter(){ public boolean isLegal(Object item){ int x = ((Integer)item).intValue(); return x % 10 == 0 ? true : false; } }); even.mapping(new Action(){ public Object doAction(Object item) { return ((Integer)item).intValue()*10; } }).mapping(new Action(){ public Object doAction(Object item){ return ((Integer)item).intValue()/2; } }).print(); Person[] person = new Person[]{ new Person("abruzzi", 25, "male"), new Person("smith", 25, "female"), new Person("json", 26, "female"), new Person("jet.lee", 25, "male") }; FPList<Person> fp = new FPList<Person>(person); fp.filter(new Filter(){ public boolean isLegal(Object item) { Person p = (Person)item; return p.getAge() == 25 ? true : false; } }).filter(new Filter(){ public boolean isLegal(Object item) { Person p = (Person)item; return p.getSex().equals("male") ? true : false; } }).mapping(new Action(){ public Object doAction(Object item) { System.err.println(((Person)item).getName()); return null; } }); } } main的运行结果如下:
index : 0, content : Apple, co
index : 0, content : 50 index : 1, content : 100 index : 2, content : 150 index : 3, content : 500 abruzzi jet.lee 上边的例子显示,匿名类在接口 中的方法不多的时候,整个匿名类整体作为一个对象传递给另外一个方法,可以很好的做到可定制性。比如第三个例子,使用Person bean的时候,可以定制多个过滤条件,依次将原始列表过滤成一个符合要求的列表。
另,文章中用到了比较多的函数式编程的概念,虽然java原生不支持,但是函数式编程作为一种思想,肯定可以在命令式的程序设计中有所体现。
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2009-12-02
啊哈,新手帖?
本意是解释下函数式的编程思想在java中的应用,并不是专门讲匿名类的,帖名可能不够贴切,不过也不至于投我新手吧? |
|
返回顶楼 | |
发表时间:2009-12-03
人民认为是新手帖就是新手帖,当年美国人民还认为不应该以每亩2美分的价钱买阿拉斯加呢。
|
|
返回顶楼 | |
发表时间:2009-12-03
Norther 写道 人民认为是新手帖就是新手帖,当年美国人民还认为不应该以每亩2美分的价钱买阿拉斯加呢。
也对,昨天晚上还有些愤愤的,后来想想,这些内容在经验更丰富,思考更深入的人眼中,又何尝不是入门级的东西呢?不过辛辛苦苦整理出来点东西,被人“轻视”,有点愤愤,也是人之常情,呵呵。 前天开会的时候,突然想到将函数式语言中的“算子”这个概念,跟java中的这个匿名类有点相似,于是散会后就速速几下,昨天又加入了些jQuery的链式操作,觉得这中代码的写法比较有意思,就分享一下。 |
|
返回顶楼 | |
发表时间:2009-12-03
我对匿名类一直是这么理解的:就是一些为了编程方便的产物,开发起来方便一点。
缺点就是复用性差,基本不考虑复用(所以干脆连名字都省了)。 所以这个是属于开发层面的东西,也没啥特别好吹捧的。 |
|
返回顶楼 | |
发表时间:2009-12-03
AllenZhang 写道 我对匿名类一直是这么理解的:就是一些为了编程方便的产物,开发起来方便一点。
缺点就是复用性差,基本不考虑复用(所以干脆连名字都省了)。 所以这个是属于开发层面的东西,也没啥特别好吹捧的。 问题并不是所有的东西都需要复用,如果总是为根本不会复用的东西创建一个类,就显得过于罗嗦了。 |
|
返回顶楼 | |
发表时间:2009-12-03
AllenZhang 写道 我对匿名类一直是这么理解的:就是一些为了编程方便的产物,开发起来方便一点。
缺点就是复用性差,基本不考虑复用(所以干脆连名字都省了)。 所以这个是属于开发层面的东西,也没啥特别好吹捧的。 大家不就是开发人员吗?所以讨论开发层面的东西很自然,没有吹捧的意思,呵呵。匿名类就是在不需要复用的时候使用的,所以文中的接口Filter和Action都是非常简单的接口。 如果Swing中的一个Action,如果在menu和toolbar中都用到的话,肯定会写成 XAction x = new XAction(),然后分别绑在menuitem和toolbar上。 可能我的表达需要好好练练,我想表达的是,将匿名类作为“一个可以做某种运算的东西(且只做某种运算)”传递给一个集合(或者叫列表),从而完成对一个集合的运算。感觉这个跟LISP的操作方式很有些相似,所以作为一个java中的fp的思考。 |
|
返回顶楼 | |
发表时间:2009-12-03
AllenZhang 写道 我对匿名类一直是这么理解的:就是一些为了编程方便的产物,开发起来方便一点。
缺点就是复用性差,基本不考虑复用(所以干脆连名字都省了)。 所以这个是属于开发层面的东西,也没啥特别好吹捧的。 饿 我到是有时遇到要多继承时用内部类~ 谁叫java不比c++呢 不过我那样设计是有毛病的~!~ |
|
返回顶楼 | |
发表时间:2009-12-03
个人感觉偶尔使用匿名类还是比较方便的,比较高内聚,但是使用的太多的话,好像代码就显得有些混乱了,总而言之,适用就好。
|
|
返回顶楼 | |
发表时间:2009-12-03
java不支持那种把代码作为参数的传递形式,
用这种方式解决问题,好像是传说中的命令模式。 |
|
返回顶楼 | |