利用Lambda表达式,方法引用以及Stream改善程序代码的可读性:
重构代码,用Lambda表达式取代匿名类
用方法引用重构Lambda表达式
用Stream API重构命令式的数据结构
从匿名类道Lambda表达式的转换
Runnable runner = new Runnble(){
public void run(){
System.out.println("Hello");
}
};
--> Runnable runner = () -> System.out.println("Hello");
匿名类和Lambda表达式中的this和super的含义是不同的。在匿名类中,this代表的是类自身,但在Lambda中,它代表的是包含类。其次匿名类可以屏蔽包含类的变量,而Lambda表达式则会报编译错误。
在涉及重载的上下文里,将匿名类转换为Lambda表达式可能导致最终的代码加晦涩。实际上匿名类的类型是在初始化时确定的,而Lambda的类型取决于它的上下文。
interface Task{
public void execute();
}
public static void doSomething(Runnable r){ r.run();}
public staitc void doSomething(Task t){ t.execute();};
-->
doSomething() -> System.out.println("Danger"); //此时两个类都是合法的目标类型
doSomething(Task) -> System.out.println("Danger"); //显式指定了需要使用的类型
采用函数式接口
可以采用有条件的延迟执行和环绕执行这两种模式重构代码,利用Lambda表达式带来灵活性。当需要从客户端代码频繁的去查询一个对象的状态是为了传递参数,调用该对象的一个方法,那么可以考虑实现一个新的方法,以Lambda或者方法表达式作为参数,新方法在检查完该对象的状态后才调用原来的方法。
有条件的延迟执行
Java8 API的log新添了一个log的重载方法,接受了一个Supplier作为参数。
public void log(Level level, Supplier<String> msgSupplier);
-->
public void log(Level level, Supplier<String> msgSupplier){
if(logger.isLoggable(level)){
log(level, msgSupplier.get());
}
}
环绕执行
当业务具有同样的准备和清理阶段,可以将这部分代码用Lambda表达式实现,这种方式的好处是可以重用准备和清理阶段的逻辑,减少重复冗余的代码。
String oneline = processFile((BufferedReader b) -> b.readLine());
String twoline = processFile((BufferedReader b) -> b.readLine() + b.readLine());
public static String processFile(BufferedReaderProcessor p) throws IOException{
try(BufferedReader br = new BufferedReader(new FileReader("text.txt"))){
return p.process(br);
}
}
public interface BufferReaderProcessor{
String process(BufferReader b) throws IOException;
}
访问者模式常用于分离程序的算法和它的操作对象。单例模式一般用于限制类的实例话,仅生成一个对象。
策略模式
策略模式代表了解决一类算法的通用解决方案,可以在运行时选择使用哪种方案。策略模式包含三部分内容:一个代表某个算法的接口;一个或多个该接口的具体实现,它们代表了算法的多种实现;一个或多个使用策略对象的客户。
模板方法
若需要采用某个算法的框架,同时又希望有一定的灵活度,能对它的某些部分进行改进,采用模版方法设计模式是比较通用的方案。
观察者模式
某些事件发生h时,主题需要自动地通知其他多个观察者。
责任链模式
责任链模式是一种创建成立对象序列的通用方案。一个处理对象可能需要在完成一些工作之后,将结果传递给另一个对象,这个对象接着做一些工作幕后再转交给下一处对象,以此类推。这种模式是通过定义一个代表处理对象的抽象类实现的,在抽象类中会定义一个字段来记录后续对象。一旦对象完成它的工作,处理对象就会将它的工作转交给它的后继。
工厂模式
无需向客户端暴漏实例化逻辑就能完成对象的创建.
因为lambda表达式没有名字,所以对lambda表达式进行调试是十分困难的。在程序异常时,编译器只能为它们指定一个名字,若方法引用指的是同一个类中生命的方法,那么它的名称是可以在栈跟踪中显示的。
对流操作中的流水线进行调试是可以调用peek方法。peek在流的每个元素恢复运行之前插入一个动作。
numbers.stream().peek(x -> System.out.println("from stream: " + x))
.map(x -> x + 7)
.peek(x -> System.out.println("after map"))
.collect(toList());