读书笔记——《Java 8实战》系列之行为参数化
2017-10-31 11:16
597 查看
转自 http://www.wxueyuan.com/blog/articles/2017/10/13/1507857576802.html
最近在读《Java 8实战》这本书,记录下来一些书中的重点知识和大家分享,文中使用了一些书中的例子,特此声明。
如果有朋友对这本书感兴趣但是又没有时间看的话,可以看我的博客来大概了解此书的重点知识。
行为参数化顾名思义就是将一段代码块(一个行为)当作参数传递给另一个方法,从而使你程序的其它部分能够使用。它最大的好处就是可以将代码推迟执行,以便于处理频繁更换的请求。
举个生活中常见的例子吧,假设我们现在有一个学生类Student
如果我们现在需要提供一个方法来筛选出所有身高在180cm以上同学,我们会怎么做呢?
这个方法当然难不住各位了,三两下就可以完成
如果此时突然需求发生了变化,要求方法筛选出所有身高在160cm以上同学,我们又要怎么做呢?
复制一遍上面的代码,将180改为160?这种方法明显不可取,如果需求又发生了变化,我们不可能再去改一遍数字吧。
这种写代码的方式不符合我们抽象化的原则。
当然大部分同学会想到解决方案,将身高当作参数放入方法当中
此时,我们就不太担心需求的变化了,无论要求我们获得身高多少以上的学生,我们都可以将它当作参数传入到方法去。
同样的道理,如果我们需要获取平均成绩在90分或80分以上的学生,只需要复制之前的代码,将height参数改为avgScore即可。
这样做看似很简单,但其实它大部分都是在进行代码的复制getStudentByHeight()和getStudentByAvgScore()两个方法的区别竟然只有传入的参数不同而已。
如果再有一个需求删选出所有年纪大于18岁的学生呢?我们难道要一直复制并修改上面的代码么
那么有没有更加优质,抽象的方法来解决这个问题呢?
答案就是今天的标题行为参数化了
首先我们先抽象一下我们目前面对的问题,我们考虑的对象是学生,而我们的需求是获取所有满足某些条件(这里的条件是学生的某些属性)的学生。
我们可以抽象出来一个方法,如果满足了所有的条件则返回true,否则返回false
像这种根据某些条件是否满足来返回一个Boolean值的函数,我们称之为谓词(predicate)。
那么我们现在来抽象出一个接口来进行建模。
针对我们需要处理的问题获取满足身高大于180cm的学生和获取满足平均成绩大于90的学生,我们可以建立两个类并实现上面的接口
那么当我们在需要筛选出符合某项条件的所有学生时,我们就可以这样写了:
在调用时,针对不同判断条件可以将不同的predicate参数放入到参数中
如果我们还需要根据其它条件返回满足条件的学生,那么我们只需要建立新的类去实现StudentPredicate接口即可,无需复制粘贴原有代码,实现了以抽象应对改变。
但是我们还不能高兴地太早,有的同学可能会发现,为了判断是否满足条件的代码当作参数传递给方法,我们现在不得不建立多个实现了StudentPredicate接口的类。
如果这些方法只被调用一两次,那么这些新被建立的类就显得有些啰嗦了。
还好,Java的匿名类机制可以帮助我们免于建立过多的只被使用一两次的类。
简单来说,Java的匿名类允许我们在声明类的同时对它进行实例化。也就是说我们不需要建立StudentHeightPredicate和StudentAvgScorePredicate这两个类了,而只需要:
看到这里同学们可能就要问了,这匿名类的机制也不是Java 8的新特性呀,为什么要在这里大费周章地讲解呢?
因为在Java 8中提供的Lambda表达式能够将上面的代码大大简化为:
是不是代码变得相当简捷,干净了?关于Lambda表达式的详解,请见博主的下一篇博客。
利用Java的泛型机制,我们还可以将我们的StudentPredicate接口在抽象的道路上走得更远。
首先再回忆一下我们的StudentPredicate
这样写接口明显不够抽象,我们能够利用泛型写出一个更加抽象的接口:
StudentFilter 也可以相应地修改为引入泛型的Filter方法
修改为:
有了这样的超级抽象版的Predicate接口,我们再也不用担心,用户的需求是返回所有满足某些条件学生,或者是返回所有满足某些条件的老师了:
总结:
行为参数化,就是一个方法接受多个不同的行为作为参数,并在内部使用它们,完成不同行为的能力
行为参数化可以让代码更好地适应不断变化的需求,减轻未来的工作量
传递代码,就是将新行为作为参数传递给方法。但在Java 8之前实现起来很啰嗦。为接口声明的许多只用一次的实体类而造成的啰嗦代码,在Java 8之前可以用匿名类消除
最近在读《Java 8实战》这本书,记录下来一些书中的重点知识和大家分享,文中使用了一些书中的例子,特此声明。
如果有朋友对这本书感兴趣但是又没有时间看的话,可以看我的博客来大概了解此书的重点知识。
行为参数化顾名思义就是将一段代码块(一个行为)当作参数传递给另一个方法,从而使你程序的其它部分能够使用。它最大的好处就是可以将代码推迟执行,以便于处理频繁更换的请求。
举个生活中常见的例子吧,假设我们现在有一个学生类Student
class Student{ private String name; //学生姓名 private int avgScore; //平均成绩 private int height; //学生身高 public Student(String name, int avgScore, int height) { super(); this.name = name; this.avgScore = avgScore; this.height = height; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAvgScore() { return avgScore; } public void setAvgScore(int avgScore) { this.avgScore = avgScore; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } }
如果我们现在需要提供一个方法来筛选出所有身高在180cm以上同学,我们会怎么做呢?
//参数students是所有学生的集合,返回一个符合条件的的 public static List<Student> getHighStudent(List<Student> students){ }
这个方法当然难不住各位了,三两下就可以完成
public static List<Student> getHighStudent(List<Student> students){ List<Student> highStudents = new ArrayList<>(); for(Student s : students) { if(s.getHeight()>=180) highStudents.add(s); } return highStudents; }
如果此时突然需求发生了变化,要求方法筛选出所有身高在160cm以上同学,我们又要怎么做呢?
复制一遍上面的代码,将180改为160?这种方法明显不可取,如果需求又发生了变化,我们不可能再去改一遍数字吧。
这种写代码的方式不符合我们抽象化的原则。
当然大部分同学会想到解决方案,将身高当作参数放入方法当中
public static List<Student> getStudentByHeight(List<Student> students, int height){ List<Student> highStudents = new ArrayList<>(); for(Student s : students) { if(s.getHeight()>=height) highStudents.add(s); } return highStudents; }
此时,我们就不太担心需求的变化了,无论要求我们获得身高多少以上的学生,我们都可以将它当作参数传入到方法去。
同样的道理,如果我们需要获取平均成绩在90分或80分以上的学生,只需要复制之前的代码,将height参数改为avgScore即可。
public static List<Student> getStudentByAvgScore(List<Student> students, int avgScore){ List<Student> highStudents = new ArrayList<>(); for(Student s : students) { if(s.getHeight()>=avgScore) highStudents.add(s); } return highStudents; }
这样做看似很简单,但其实它大部分都是在进行代码的复制getStudentByHeight()和getStudentByAvgScore()两个方法的区别竟然只有传入的参数不同而已。
如果再有一个需求删选出所有年纪大于18岁的学生呢?我们难道要一直复制并修改上面的代码么
那么有没有更加优质,抽象的方法来解决这个问题呢?
答案就是今天的标题行为参数化了
首先我们先抽象一下我们目前面对的问题,我们考虑的对象是学生,而我们的需求是获取所有满足某些条件(这里的条件是学生的某些属性)的学生。
我们可以抽象出来一个方法,如果满足了所有的条件则返回true,否则返回false
像这种根据某些条件是否满足来返回一个Boolean值的函数,我们称之为谓词(predicate)。
那么我们现在来抽象出一个接口来进行建模。
interface StudentPredicate{ //由于我们需要满足的条件是学生的属性,因此传入的参数直接写为Student实例即可 boolean test(Student s); }
针对我们需要处理的问题获取满足身高大于180cm的学生和获取满足平均成绩大于90的学生,我们可以建立两个类并实现上面的接口
class StudentHeightPredicate implements StudentPredicate{ @Override public boolean test(Student s) { if(s.getHeight()>=180) return true; return false; } } class StudentAvgScorePredicate implements StudentPredicate{ @Override public boolean test(Student s) { if(s.getAvgScore()>=90) return true; return false; } }
那么当我们在需要筛选出符合某项条件的所有学生时,我们就可以这样写了:
public static List<Student> studentFilter(List<Student> students, StudentPredicate predicate){ List<Student> highStudents = new ArrayList<>(); for(Student s : students) { //如果谓词方法中的条件得到满足则返回true if(predicate.test(s)) highStudents.add(s); } return highStudents; }
在调用时,针对不同判断条件可以将不同的predicate参数放入到参数中
//返回身高超过180cm的学生 List<Student> filteredStudents = studentFilter(students, new StudentHeightPredicate()); //返回身高超过90cm的学生 List<Student> filteredStudents = studentFilter(students, new StudentAvgScorePredicate());
如果我们还需要根据其它条件返回满足条件的学生,那么我们只需要建立新的类去实现StudentPredicate接口即可,无需复制粘贴原有代码,实现了以抽象应对改变。
但是我们还不能高兴地太早,有的同学可能会发现,为了判断是否满足条件的代码当作参数传递给方法,我们现在不得不建立多个实现了StudentPredicate接口的类。
如果这些方法只被调用一两次,那么这些新被建立的类就显得有些啰嗦了。
还好,Java的匿名类机制可以帮助我们免于建立过多的只被使用一两次的类。
简单来说,Java的匿名类允许我们在声明类的同时对它进行实例化。也就是说我们不需要建立StudentHeightPredicate和StudentAvgScorePredicate这两个类了,而只需要:
//返回身高超过180cm的学生 List<Student> filteredStudents = studentFilter(students, new StudentPredicate() { @Override public boolean test(Student s) { if(s.getHeight()>=180) return true; return false; } }); //返回身高超过90cm的学生 List<Student> filteredStudents = studentFilter(students, new StudentPredicate() { @Override public boolean test(Student s) { if(s.getAvgScore()>=90) return true; return false; } });
看到这里同学们可能就要问了,这匿名类的机制也不是Java 8的新特性呀,为什么要在这里大费周章地讲解呢?
因为在Java 8中提供的Lambda表达式能够将上面的代码大大简化为:
List<Student> filteredStudents2 = studentFilter(students, Student s -> s.getHeight()>=180 );
是不是代码变得相当简捷,干净了?关于Lambda表达式的详解,请见博主的下一篇博客。
利用Java的泛型机制,我们还可以将我们的StudentPredicate接口在抽象的道路上走得更远。
首先再回忆一下我们的StudentPredicate
interface StudentPredicate{ //由于我们需要满足的条件是学生的属性,因此传入的参数直接写为Student实例即可 boolean test(Student s); }
这样写接口明显不够抽象,我们能够利用泛型写出一个更加抽象的接口:
interface Predicate<T>{ //这里使用泛型来传入对象 boolean test(T t); }
StudentFilter 也可以相应地修改为引入泛型的Filter方法
public static List<Student> studentFilter(List<Student> students, StudentPredicate predicate){ List<Student> highStudents = new ArrayList<>(); for(Student s : students) { //如果谓词方法中的条件得到满足则返回true if(predicate.test(s)) highStudents.add(s); } return highStudents; }
修改为:
public static <T> List<T> filter(List<T> list, Predicate predicate){ List<T> result = new ArrayList<>(); for(T t : list) { //如果谓词方法中的条件得到满足则返回true if(predicate.test(t)) result.add(t); } return result; }
有了这样的超级抽象版的Predicate接口,我们再也不用担心,用户的需求是返回所有满足某些条件学生,或者是返回所有满足某些条件的老师了:
//返回所有均分90以上的学生 匿名类写法 List<Student> filteredStudents = filter(students, new Predicate(){ @Override public boolean test(Student s) { if(s.getAvgScore()>=90) return true; return false; } }); //返回所有均分90以上的学生 Lambda表达式写法 List<Student> filteredStudents = filter(students, Student s -> s.getAvgScore()>=90 ); //返回所有工资5000以上的老师 匿名类写法 List<Teacher> filteredTeachers = filter(teachers, new Predicate(){ @Override public boolean test(Teacher t) { if(t.getAvgSalary()>=5000) return true; return false; } }); //返回所有工资5000以上的老师 Lambda表达式写法 List<Teacher> filteredTeachers = filter(teachers, Teacher t -> t.getAvgSalary()>=5000 );
总结:
行为参数化,就是一个方法接受多个不同的行为作为参数,并在内部使用它们,完成不同行为的能力
行为参数化可以让代码更好地适应不断变化的需求,减轻未来的工作量
传递代码,就是将新行为作为参数传递给方法。但在Java 8之前实现起来很啰嗦。为接口声明的许多只用一次的实体类而造成的啰嗦代码,在Java 8之前可以用匿名类消除
相关文章推荐
- 读书笔记——《Java 8实战》系列之Lambda表达式(一)
- java8实战一:通过行为参数化传递代码
- java 8 系列之 01 行为参数化
- 《Java 8 实战》 学习笔记一(行为参数化)
- JAVA8实战 第二章行为参数化
- 读书笔记——《Java 8实战》系列之方法引用
- 读书笔记——《Java 8实战》系列之Lambda表达式(二)
- 【Head First Java 读书笔记】(四)对象的行为
- Java8-行为参数化
- Java 8实战之读书笔记一:内容简介
- 【云星数据---mesos实战系列002】:mesos全分布式部署实战002--Java准备
- 读书笔记:Java并发实战第15章 原子变量与非阻塞同步机制
- Android应用开发提高系列(1)——《Practical Java 中文版》读书笔记(上)
- Java__线程---基础知识全面实战---坦克大战系列为例
- Java 调用 C/C++ 之 JNA 系列实战篇 —— 起步 (一)
- Android应用开发提高系列(2)——《Practical Java 中文版》读书笔记(下)
- java8行为参数化/lambda实现示例
- Android应用开发提高系列(1)——《Practical Java 中文版》读书笔记(上)
- Java 调用 C/C++ 之 JNA 系列实战篇 —— 输入char * (五)
- Java 8 之 行为参数化