和风细雨

世上本无难事,心以为难,斯乃真难。苟不存一难之见于心,则运用之术自出。

嵌套类和匿名类

嵌套类和匿名类

内部类的出现
当进行Java开发时,有时需要实现一个仅包含1-2个方法的接口.在AWT和Swing开发中经常出现这种情况,例如当一个display组件需要一个事件回调方法如一个按钮的ActionListener时. 如果使用普通的类来实现此操作,最终会得到很多仅在单个位置上使用的小型类.
内部类用于处理这种情况,java允许定义内部类,而且可在Gui外使用内部类.

内部类的定义和实现

内部类是指在另一个类内部定义的一个类.可以将内部类定义为一个类的成员.
public class Linker{
  public class LinkedNode{
    private LinkedNode prev;
    private LinkedNode next;
    private String content;
   
    public LinkedNode(String content){
      this.content=content;
    }
  }
 
  public Linker(){
    LinkedNode first=new LinkedNode("First");
    LinkedNode second=new LinkedNode("Second");
   
    first.next=second;
    second.prev=first;
  }
}

定义在一个类方法中的内部类

public class Hapiness{
  interface Smiler{
    public void smile();
  }
 
  public static void main(String[] args){
    class Happy implements Smiler{
      public void smile(){
        System.out.println(":-}");
      }
    }
   
    Happy happy=new Happy();
    happy.smile();
  }
}

匿名类

对很多情况而言,定义在方法内部的类名意义不大,它可以保持为匿名的,程序员关心的只是它的实例名.
如:
Runnable runner=new Runnable(){
     public void  run(){
          // Run statememnt
     }
}

理解匿名类

匿名类并不难理解,它只是把类的定义过程和实例的创建过程混合而已,上页的语句实际上相当于如下语句:
// 定义类
Public class Runner implements Runnable{
     public void run(){
         // do sth
      }
}

// 创建实例
Runner runner=new Runner();

使用匿名类的筛选解耦过程

需求:从公司的职员列表中,找出男性且年龄大于22的成员.

传统写法:

   List allmembers=company.getMembers();// 取得所有成员
    List results=new ArrayList();// 结果列表
   
    for(Iterator it=allmembers.iterator();it.hasNext();){
     Member member=(Member)it.next();
    
     if(member.getAge()>22 && member.isMale()){  // 筛选,这里是把查询条件和遴选过程融合在一起,条件一变立即就得加个分支.
      results.add(member);
     }
   }

传统方法的缺陷

这种写法没有错,但是不是面向对象的写法,它有以下缺陷:
1.查询条件和筛选过程没有分离.
2.这样写的后果使Company变成了一个失血模型而不是领域模型.
3.换查询条件的话,上面除了"筛选"一句有变化外其它都是模板代码,重复性很高.

使用匿名类实现的OO化查询

真正符合OO的查询应该是这样:

   MemberFilter filter1=new MemberFilter(){
    public boolean accept(Member member) {
         return member.isMale() && member.getAge()>22;
    }
   };
  
   List ls=company.listMembers(filter1);
 

这段代码成功的把查询条件作为一个接口分离了出去,接口代码如下:

public interface MemberFilter{
  public boolean accept(Member member);
}

查询函数的变化

而类Company增加了这样一个函数:

public List searchMembers(MemberFilter memberFilter){
   List retval=new ArrayList();
   
    for(Iterator it=members.iterator();it.hasNext();){
     Member member=(Member)it.next();
    
     if(memberFilter.accept(member)){
      retval.add(member);
    }
   } 
  
   return retval;
}
这就把模板代码归结到了类内部,外面不会重复书写了.Company也同时拥有了数据和行为,而不是原来的数据容器了.


匿名类的例子二

用匿名类处理分类汇总的方法 分类汇总是统计中常用,举例来说如统计学生成绩,及格不及格的归类,分优良中差等级归类等,每个单项代码很好写,但是如果分类汇总的项目多了,能一种汇总写一个函数吗? 比如说有些科目60分才算及格,有些科目50分就算;有些老师喜欢分优良中差四等,有些老师却喜欢分ABCD;不一而足,如果每个都写一个函数无疑是个编写和维护恶梦. 如果我们用匿名类把分类汇总的规则和分类汇总的过程分别抽象出来,代码就清晰灵活多了,以下代码讲述了这个过程.

基本类Student

public class Student{
    private String name;
    private int score;
   
    public Student(String name,int score){
        this.name=name;
        this.score=score;
    }
   
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getScore() {
        return score;
    }
    public void setScore(int score) {
        this.score = score;
    }   
}


用于分类汇总的类

它强制子类实现getKey和getvalue两个方法:
public abstract class ClassifyRule {
    public Student student;
   
    public ClassifyRule(){       
    }   

    public void setStudent(Student student) {
        this.student = student;
    }
   
    abstract public String getKey();
    abstract public int getValue();
}

对Student进行统计处理的StudentService类

注意getSum方法,它保留了筛选过程,筛选规则则不在其中:
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

public class StudentService {
    private List<Student> students;

    public StudentService() {
        students = new ArrayList<Student>();
    }

    public void add(Student student) {
        students.add(student);
    }

    public Hashtable<String, Integer> getSum(ClassifyRule rule) {
        Hashtable<String, Integer> ht = new Hashtable<String, Integer>();

        for (Student student : students) {
            rule.setStudent(student);
            String key = rule.getKey();
            int value = rule.getValue();

            if (ht.containsKey(key)) {
                Integer oldValue = ht.remove(key);
                oldValue += value;
                ht.put(key, oldValue);
            } else {
                ht.put(key, value);
            }
        }

        return ht;
    }
}

测试代码,注意其中筛选规则的创建

public class Test {
    public static void main(String[] args) {
        // 初始化
        StudentService service = new StudentService();
        service.add(new Student("Andy", 90));
        service.add(new Student("Bill", 95));
        service.add(new Student("Cindy", 70));
        service.add(new Student("Dural", 85));
        service.add(new Student("Edin", 60));
        service.add(new Student("Felix", 55));
        service.add(new Student("Green", 15));

        // 60分及格筛选
        ClassifyRule rule60 = new ClassifyRule() {
            public String getKey() {
                return student.getScore() >= 60 ? "及格" : "不及格";
            }

            public int getValue() {
                return 1;
            }
        };

        System.out.println("60分及格筛选");
        printHt(service.getSum(rule60));

        // 50分及格筛选
        ClassifyRule rule50 = new ClassifyRule() {
            public String getKey() {
                return student.getScore() >= 50 ? "及格" : "不及格";
            }

            public int getValue() {
                return 1;
            }
        };
        System.out.println("\n50分及格筛选");
        printHt(service.getSum(rule50));

        // 分"优良中差"等级
        ClassifyRule ruleCn = new ClassifyRule() {
            public String getKey() {
                String retval = "";

                int score = student.getScore();
                if (score >= 90) {
                    retval = "优";
                } else if (score >= 80) {
                    retval = "良";
                } else if (score >= 60) {
                    retval = "中";
                } else if (score > 0) {
                    retval = "差";
                }

                return retval;
            }

            public int getValue() {
                return 1;
            }
        };

测试代码

System.out.println("\n分优良中差等级筛选");
        printHt(service.getSum(ruleCn));

        // 分"ABCD"等级
        ClassifyRule ruleWest = new ClassifyRule() {
            public String getKey() {
                String retval = "";

                int score = student.getScore();
                if (score >= 90) {
                    retval = "A";
                } else if (score >= 80) {
                    retval = "B";
                } else if (score >= 60) {
                    retval = "C";
                } else if (score > 0) {
                    retval = "D";
                }

                return retval;
            }

            public int getValue() {
                return 1;
            }
        };

        System.out.println("\n分ABCD等级筛选");
        printHt(service.getSum(ruleWest));
    }

    private static void printHt(Hashtable ht) {
        for (Iterator it = ht.keySet().iterator(); it.hasNext();) {
            String key = (String) it.next();
            Integer value = (Integer) ht.get(key);
            System.out.println("Key=" + key + " Value=" + value);
        }
    }
}


测试结果如下:

 

60分及格筛选
Key=及格 Value=5
Key=不及格 Value=2

50分及格筛选
Key=及格 Value=6
Key=不及格 Value=1

分优良中差等级筛选
Key=优 Value=2
Key=良 Value=1
Key=中 Value=2
Key=差 Value=2

分ABCD等级筛选
Key=A Value=2
Key=D Value=2
Key=C Value=2
Key=B Value=1

后记

内部类也叫嵌套类,一般不提倡书写,但它在java核心类中都存在,如接口Map中的Entry,我们应该了解并能解读这种方法.

匿名类相对而言有用得多,在解耦合和事件回调注册中很常见,大家应该对它的运用融会贯通.

posted on 2008-03-03 22:30 和风细雨 阅读(304) 评论(0)  编辑  收藏 所属分类: J2SE


只有注册用户登录后才能发表评论。


网站导航: