引言
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 [email protected]
*
*/
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 [email protected]
*
*/
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 : 50
index : 1, content : 100
index : 2, content : 150
index : 3, content : 500
abruzzi
jet.lee
上邊的例子顯示,匿名類在接口 中的方法不多的時候,整個匿名類整體作爲一個對象傳遞給另外一個方法,可以很好的做到可定製性。比如第三個例子,使用Person bean的時候,可以定製多個過濾條件,依次將原始列表過濾成一個符合要求的列表。
另,文章中用到了比較多的函數式編程的概念,雖然java原生不支持,但是函數式編程作爲一種思想,肯定可以在命令式的程序設計中有所體現。