`
abruzzi
  • 浏览: 445391 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

对Java中匿名类的一些思考

阅读更多

引言

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原生不支持,但是函数式编程作为一种思想,肯定可以在命令式的程序设计中有所体现。

 

分享到:
评论
20 楼 congdepeng 2009-12-07  
建议各位看看《冒号课堂——编程范式与OOP思想》,写的不错。

第一部分关于paradigm的,写的不错
19 楼 天一 2009-12-04  
看看guice的代码。
18 楼 transist 2009-12-04  
貌似偏激的技术人员不少。无论在csdn还是在java eye上,只要是有人提出观点,回帖中很多人纯粹为拍砖而拍砖。世界因为色彩多而缤纷绚丽。
17 楼 fengsky491 2009-12-04  
这就是java的回调接口的实现吧
16 楼 rplees 2009-12-03  
加点佐料会让‘生活’更加的有味道。
15 楼 abruzzi 2009-12-03  
chandler 写道
   java,或者说面向对象的优势在于易读,易改,易扩展。你觉得匿名类占哪条?


呵呵,不要教条主义,也不要用静态的眼光看事物。将别的比较好的语言风格融入java有何不可?java 7不也引入闭包吗,你说闭包在易读,易改方面,占了哪条?
14 楼 carlkkx 2009-12-03  
chandler 写道
   java,或者说面向对象的优势在于易读,易改,易扩展。你觉得匿名类占哪条?

你为什么一定要纯粹的面向对象呢?谁说java又一定要纯粹的面向对象呢?
13 楼 chandler 2009-12-03  
   java,或者说面向对象的优势在于易读,易改,易扩展。你觉得匿名类占哪条?
12 楼 Norther 2009-12-03  
Norther 写道
chandler 写道
  一直觉得匿名类是可有可无的东西。无非就是某些人少些点代码。偷懒的借口而已。


一直觉得spring, hibernate, struts是可有可无的东西,无非就是某些人少些点吗。偷懒的接口而已。


一直觉得汽车是可有可无的东西,无非就是某些人少走些路,偷懒的接口而已。
11 楼 Norther 2009-12-03  
chandler 写道
  一直觉得匿名类是可有可无的东西。无非就是某些人少些点代码。偷懒的借口而已。


一直觉得spring, hibernate, struts是可有可无的东西,无非就是某些人少些点吗。偷懒的接口而已。
10 楼 chandler 2009-12-03  
  一直觉得匿名类是可有可无的东西。无非就是某些人少些点代码。偷懒的借口而已。
9 楼 aoliwen521 2009-12-03  
java不支持那种把代码作为参数的传递形式,
用这种方式解决问题,好像是传说中的命令模式。
8 楼 寄生虫 2009-12-03  
个人感觉偶尔使用匿名类还是比较方便的,比较高内聚,但是使用的太多的话,好像代码就显得有些混乱了,总而言之,适用就好。
7 楼 whaosoft 2009-12-03  
AllenZhang 写道
我对匿名类一直是这么理解的:就是一些为了编程方便的产物,开发起来方便一点。
缺点就是复用性差,基本不考虑复用(所以干脆连名字都省了)。
所以这个是属于开发层面的东西,也没啥特别好吹捧的。

饿 我到是有时遇到要多继承时用内部类~ 谁叫java不比c++呢
不过我那样设计是有毛病的~!~
6 楼 abruzzi 2009-12-03  
AllenZhang 写道
我对匿名类一直是这么理解的:就是一些为了编程方便的产物,开发起来方便一点。
缺点就是复用性差,基本不考虑复用(所以干脆连名字都省了)。
所以这个是属于开发层面的东西,也没啥特别好吹捧的。


大家不就是开发人员吗?所以讨论开发层面的东西很自然,没有吹捧的意思,呵呵。匿名类就是在不需要复用的时候使用的,所以文中的接口Filter和Action都是非常简单的接口。

如果Swing中的一个Action,如果在menu和toolbar中都用到的话,肯定会写成
XAction x = new XAction()
,然后分别绑在menuitem和toolbar上。

可能我的表达需要好好练练,我想表达的是,将匿名类作为“一个可以做某种运算的东西(且只做某种运算)”传递给一个集合(或者叫列表),从而完成对一个集合的运算。感觉这个跟LISP的操作方式很有些相似,所以作为一个java中的fp的思考。
5 楼 Norther 2009-12-03  
AllenZhang 写道
我对匿名类一直是这么理解的:就是一些为了编程方便的产物,开发起来方便一点。
缺点就是复用性差,基本不考虑复用(所以干脆连名字都省了)。
所以这个是属于开发层面的东西,也没啥特别好吹捧的。



问题并不是所有的东西都需要复用,如果总是为根本不会复用的东西创建一个类,就显得过于罗嗦了。
4 楼 AllenZhang 2009-12-03  
我对匿名类一直是这么理解的:就是一些为了编程方便的产物,开发起来方便一点。
缺点就是复用性差,基本不考虑复用(所以干脆连名字都省了)。
所以这个是属于开发层面的东西,也没啥特别好吹捧的。
3 楼 abruzzi 2009-12-03  
Norther 写道
人民认为是新手帖就是新手帖,当年美国人民还认为不应该以每亩2美分的价钱买阿拉斯加呢。

也对,昨天晚上还有些愤愤的,后来想想,这些内容在经验更丰富,思考更深入的人眼中,又何尝不是入门级的东西呢?不过辛辛苦苦整理出来点东西,被人“轻视”,有点愤愤,也是人之常情,呵呵。

前天开会的时候,突然想到将函数式语言中的“算子”这个概念,跟java中的这个匿名类有点相似,于是散会后就速速几下,昨天又加入了些jQuery的链式操作,觉得这中代码的写法比较有意思,就分享一下。
2 楼 Norther 2009-12-03  
人民认为是新手帖就是新手帖,当年美国人民还认为不应该以每亩2美分的价钱买阿拉斯加呢。
1 楼 abruzzi 2009-12-02  
啊哈,新手帖?
本意是解释下函数式的编程思想在java中的应用,并不是专门讲匿名类的,帖名可能不够贴切,不过也不至于投我新手吧?

相关推荐

    java认证所有课程

    说到底,所有的组件都继承了java.awt.Component类,而handleEvent()就在java.awt.Component类中。 然而,这种模型也存在缺点: - 事件只能由产生这个事件的组件或包含这个组件的容器处理。这个限制违反了面向对象...

    thinkinjava源码-ThinkingInJava:用Java源代码思考

    匿名内部类编译时,会单独生成一个class文件,该类持有外部类的引用,传入的参数都会自动复制一份。那么传入的参数 与 内部类实际使用的参数就不是相同的一个参数了, 为了避免出现问题,所以规定匿名内部类使用的...

    深入理解Android:卷I--详细书签版

     第5章讲解了Android源码中常用的类,如sp、wp、RefBase、Thread类、同步类、Java中的Handler类以及Looper类。这些类都是Android中最常用和最基本的,只有掌握这些类的知识,才 能在分析后续的代码时游刃有余。 ...

    实验项目C、面向抽象和面向接口编程

    3、理解和掌握匿名类的用法。 4、初步了解JDK8新特性和lambda表达式。 二、实验内容和主要步骤 ★专题:继承基础和面向抽象编程★ 1.继承基础实验 2.Object类使用 3.抽象类和抽象方法 4.继承机制实现的多态 ★专题...

    《深入理解Android》卷Ⅰ

    2.4.8 JNI中的异常处理 2.5 本章小结 第3章 深入理解init 3.1 概述 3.2 init分析 3.2.1 解析配置文件 3.2.2 解析service 3.2.3 init控制service 3.2.4 属性服务 3.3 本章小结 第4章 深入理解zygote 4.1 概述 4.2 ...

    Android实验六.doc

    【思考题】 1. 发送广播消息的3中方法是什么? 1) onSendStaticBroadcast(View v); 2) onSendDynamicBroadcast(View v); 3) onSendStickyBroadcast(View v)。 得分(百分制) ----------------------- Android...

    Functional-Programming-Java

    Java函数式编程 第一法则:在学习任何新...匿名内部类vs Lambda表达式 默认方法 静态方法() 预定义的功能接口 一个论点已预先确定 谓词 功能 消费者 供应商 预定义的两个论点 双谓词 双功能 双消费 额外的预定义 I

    Scala程序设计(第2版)

    22.1 在Scala代码中使用Java名称 430 22.2 Java泛型与Scala泛型 430 22.3 JavaBean的性质 432 22.4 AnyVal类型与Java原生类型 433 22.5 Java代码中的Scala名称 433 22.6 本章回顾与下一章提要 434 ...

    c#学习笔记.txt

    以至于正当Microsoft尽力在Visual J++基础上拓展Java功能,并使之与Windows操作系统紧密结合在一起的时候,Sun公司对Microsoft提出了法律诉讼,控告Microsoft违反了许可证协议中的条款,最终的结果是Microsoft公司...

    深入理解Android卷1全

    2.4.8 JNI中的异常处理 / 32 2.5 本章小结 / 32 第3章 深入理解init / 33 3.1 概述 / 34 3.2 init分析 / 34 3.2.1 解析配置文件 / 38 3.2.2 解析service / 42 3.2.3 init控制service / 48 3.2.4 属性服务 / 52 3.3 ...

    TaskExecutor:TaskExecutor是健壮,统一和集中式异步Task执行框架的实现。 任务将保留在磁盘上,以适应configurationChanges,创建新的Activity,甚至在进程终止后都可以生存。 通过许多选项,几乎可以保证您的任务可以执行,并通过硬回调直接回发到当前活动

    不再需要匿名线程,不必在一个Activity中启动线程,也不必去思考关于如果用户打开了一个新的活动,完成后会发生什么。 您是否担心onProgressUpdate()中的空指针? 如果用户在线程运行时启动新的活动,会发生什么...

    基于j2ee的ajax宝典

    1.1 重新思考Web应用····· 2 1.1.1 应用系统的发展史············· 2 1.1.2 传统Web应用的优势和缺点··············· 4 1.2 重新设计Web应用····· 5 1.2.1 RIA应用·····...

    Linux多线程服务端编程:使用muduo C++网络库

    4.4.2exit(3) 在C++ 中不是线程安全的.. . . . . . . . . . . . . . . . 94 4.5善用__thread 关键字.. . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 4.6多线程与IO . . . . . . . . . . . . . . . . ....

Global site tag (gtag.js) - Google Analytics