优缺点
优点:
- 简洁
- 非常容易并行计算(Stream)
- 利于编译器优化(猜测是因为编译是不用解析字面量,可能还有 Java 7 中新加的 invokedynamic 指令动态绑定)
- 可传递行为(函数编程),而不仅仅是值
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
public int sumAll(List<Integer> numbers) {
int total = 0;
for (int number : numbers) {
total += number;
}
return total;
}
sumAll 算法很简单,完成的是将 List 中所有元素相加。某一天如果需要增加一个对 List 中所有偶数求和的方法 sumAllEven,如下:
public int sumAllEven(List<Integer> numbers) {
int total = 0;
for (int number : numbers) {
if (number % 2 == 0) {
total += number;
}
}
return total;
}
又有一天,我们需要增加第三个方法:对 List 中所有大于 3 的元素求和,那是不是继续加下面的方法呢
public int sumAllEven(List<Integer> numbers) {
int total = 0;
for (int number : numbers) {
if (number > 3) {
total += number;
}
}
return total;
}
比较这三个方法,我们发现了一个很明显的 “代码臭味” —— 代码重复(详情参考《重构》),三个方法的唯一区别在于 if 判断这一行代码。如果脱离这里的上下文,我们会怎么做呢?我首先会先想到利用策略模式重构代码如下:
public interface Strategy {
public boolean test(int num);
}
public class SumAllStrategy implements Strategy {
public boolean test(int num) {
return true;
}
}
public class SumAllEvenStrategy implements Strategy {
public boolean test(int num) {
return num % 2 == 0;
}
}
public class ContextClass {
private Strategy stragegy = null;
private final static Strategy DEFAULT_STRATEGY = new SumAllStrategy();
public ContextClass() {
this(null);
}
public ContextClass(Stragegy stragegy) {
if(strategy != null) {
this.strategy = strategy;
}
else {
this.strategy = DEFAULT_STRATEGY;
}
}
public int sumAll(List<Integer> numbers) {
int total = 0;
for (int number : numbers) {
if (strategy.test(number)) {
total += number;
}
}
return total;
}
}
// 调用
ContextClass context = new ContextClass();
context.sumAll(numbers);
设计模式在这里发挥了作用,OO特性还是蛮强大的!但这是唯一的解决方案吗(当然不考虑用其他设计模式来解决,因为都是 OO 范畴)?当然有,该轮到 Java 8 Lambda 表达式中的谓词(Predicate)发挥作用了!
public int sumAll(List<Integer> numbers, Predicate<Integer> p) {
int total = 0;
for (int number : numbers) {
if (p.test(number)) {
total += number;
}
}
return total;
}
sumAll(numbers, n -> true);
sumAll(numbers, n -> n % 2 == 0);
sumAll(numbers, n -> n > 3);
缺点:
- 若不用并行计算,很多时候计算速度没有比传统的 for 循环快(并行计算有时需要预热才显示出效率优势)
- 不容易调试
- 在 lambda 语句中强制类型转换貌似不方便,一定要搞清楚到底是 map 还是 mapToDouble 还是 mapToInt
Lambda 表达式与匿名类的区别
使用匿名类与 Lambda 表达式的一大区别在于关键词的使用。对于匿名类,关键词 this 解读为匿名类,而对于 Lambda 表达式,关键词 this 解读为写就 Lambda 的外部类
Lambda 表达式与匿名类的另一不同在于两者的编译方法。Java 编译器编译 Lambda 表达式并将他们转化为类里面的私有函数,它使用 Java 7 中新加的 invokedynamic 指令动态绑定该方法