疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

软件开发培训—Java 8为什么需要Lambda表达式


 

   函数编程在C#、Python、JavaScript中都得到充分体现。而Java直到最新的Java 8才开始正式支持函数编程,最明显的改进就是对Lamba表达式的支持。正如C#之父Anders Hejlsberg在那篇文章 编程语言大趋势 中所讲,未来的编程语言将逐渐融合各自的特性,而不存在单纯的声明式语言(如之前的Java)或者单纯的函数编程语言。将来声明式编程语言借鉴函数编程思想,函数编程语言融合声明式编程特性...这几乎是一种必然趋势。如下图所示:

  那具体而言我们为什么需要Lambda表达式呢?难道Java的OO和命令式编程(imperative programming)特性不够强大吗?下面让我们来分析下其原因。

  1、内部循环和外部循环

  先看一个大家耳熟能详的例子:

  view source

  print?

  1 List numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

  2

  3 for (int number : numbers) {

  4 System.out.println(number);

  5 }

  是不是很常见呢?这个叫外部循环(External Iteration)。但是外部循环有什么问题呢?简

  单来说存在下面三个缺点:

  只能顺序处理List中的元素(process one by one)

  不能充分利用多核CPU

  不利于编译器优化

  而如果利用内部循环,代码写成下面这样:

  view source

  print?

  1 List numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

  2

  3 numbers.forEach((Integer value) -> System.out.println(value));

  这样就能规避上面的三个问题:

  不一定需要顺序处理List中的元素,顺序可以不确定

  可以并行处理,充分利用多核CPU的优势

  有利于JIT编译器对代码进行优化

  类似的C#从4.0版本开始也支持集合元素并行处理,代码如下:

  view source

  print?

  1 List nums = new List { 1, 2, 3, 4, 5, 6 };

  2 Parallel.ForEach(nums, (value) =>

  3 {

  4 Console.WriteLine(value);

  5 });

  2、传递行为,而不仅仅是传值

  如果你使用C#有一段时间的话,那么你很可能已经明白这个标题的意思了。在C#中,经常看

  到一些函数的参数是Action或者Func类型,比如下面这个:

  view source

  print?

  01 public class ArticleDac {

  02 ...

  03 public Article GetArticles(Func, Article> func) // 这里传

  递的就是行为

  04 {

  05 using(var db = xx) {

  06 return func(db.Articles);

  07 }

  08 }

  09 ...

  10 }

  11 // 下面是调用

  12 int articleId = 119;

  13 var firstArticle = new ArticleDac().GetArticles(

  14 articleDbSet =>

  15 articleDbSet.AsQueryable().FirstOrDefault(x => x.id == articleId)

  16 );

  看不懂?没关系。我们先来看一个体现传值局限性的场景吧,上代码:

  view source

  print?

  1 List numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

  2

  3 public int sumAll(List numbers) {

  4 int total = 0;

  5 for (int number : numbers) {

  6 total += number;

  7 }

  8 return total;

  9 }

  sumAll算法很简单,完成的是将List中所有元素相加。某一天如果我们需要增加一个对List

  中所有偶数求和的方法sumAllEven,如下:

  view source

  print?

  1 public int sumAllEven(List numbers) {

  2 int total = 0;

  3 for (int number : numbers) {

  4 if (number % 2 == 0) {

  5 total += number;

  6 }

  7 }

  8 return total;

  9 }

  又有一天,我们需要增加第三个方法:对List中所有大于3的元素求和,那是不是继续加下面

  的方法呢?

  view source

  print?

  1 public int sumAllEven(List numbers) {

  2 int total = 0;

  3 for (int number : numbers) {

  4 if (number > 3) {

  5 total += number;

  6 }

  7 }

  8 return total;

  9 }

  比较这三个方法,我们发现了一个很明显的“代码臭味”—— 代码重复(详情参考《重构》

  ),三个方法的唯一区别在于if判断这一行代码。如果脱离这里的上下文,我们会怎么做呢?我首

  先会先想到利用策略模式重构代码如下:

  view source

  print?

  01 public interface Strategy {

  02 public boolean test(int num);

  03 }

  04

  05 public class SumAllStrategy implements Strategy {

  06 public boolean test(int num) {

  07 return true;

  08 }

  09 }

  10

  11 public class SumAllEvenStrategy implements Strategy {

  12 public boolean test(int num) {

  13 return num % 2 == 0;

  14 }

  15 }

  16

  17 public class ContextClass {

  18 private Strategy stragegy = null;

  19 private final static Strategy DEFAULT_STRATEGY = new SumAllStrategy();

  20

  21 public ContextClass() {

  22 this(null);

  23 }

  24

  25 public ContextClass(Stragegy stragegy) {

  26 if(strategy != null) {

  27 this.strategy = strategy;

  28 }

  29 else {

  30 this.strategy = DEFAULT_STRATEGY;

  31 }

  32 }

  33

  34 public int sumAll(List numbers) {

  35 int total = 0;

  36 for (int number : numbers) {

  37 if (strategy.test(number)) {

  38 total += number;

  39 }

  40 }

  41

  42 return total;

  43 }

  44 }

  45

  46

  47 // 调用

  48 ContextClass context = new ContextClass();

  49 context.sumAll(numbers);

  设计模式在这里发挥了作用,OO特性还是蛮强大的!但这是唯一的解决方案吗(当然不考虑用其他设计模式来解决,因为都是OO范畴!)?当然有,该轮到Java 8 Lambda表达式中的谓词

  (Predicate)发挥作用了!

  view source

  print?

  01 public int sumAll(List numbers, Predicate p) {

  02 int total = 0;

  03 for (int number : numbers) {

  04 if (p.test(number)) {

  05 total += number;

  06 }

  07 }

  08 return total;

  09 }

  10

  11 sumAll(numbers, n -> true);

  12 sumAll(numbers, n -> n % 2 == 0);

  13 sumAll(numbers, n -> n > 3);