美文网首页
JAVA8新特性之不得不说的lambda表达式

JAVA8新特性之不得不说的lambda表达式

作者: 先生zeng | 来源:发表于2020-11-29 14:40 被阅读0次

    本篇内容

    • 函数式编程
    • 行为参数化
    • 方法引用
    • 匿名函数
    • 函数式接口
    • 一步步演变-lambda表达式

    讲真的,在整理这篇文章内容之前,我一直在想要不要整理的这么细节,但俗话说得好,细节决定成败,所以我希望每个对lambda表达式没有概念的同学看过我这篇文章后,都能跟看过一遍java8实战这本书一样,对于lambda表达式出现的前因后果有个比较深入的了解跟理解,同时也能够让已经看过java8实战这本书的内容的人,进一步深入lambda表达式这个概念以及设计的意义。

    函数式编程

    在JAVA8中引入了比较前沿的就是函数式编程的思想,我们本篇要讲的lambda表达式,一步步演变之前,最开始都是基于我现在说的这个函数式编程的思想。所谓函数式编程,是一种编程范式,我们常见的编程范式有命令式编程(Imperative programming),函数式编程,逻辑式编程,常见的面向对象编程是也是一种命令式编程。

    命令式编程是面向计算机硬件的抽象,有变量(对应着存储单元),赋值语句(获取,存储指令),表达式(内存引用和算术运算)和控制语句(跳转指令),一句话,命令式程序就是一个冯诺依曼机的指令序列。

    ** 而函数式编程是面向数学的抽象,将计算描述为一种表达式求值,一句话,函数式程序就是一个表达式。**
    很抽象哈,其实就是你可以把函数当成参数去对待,参数可以承担的角色,函数也可以。在函数式语言中,函数作为"一等公民"(这里是指向),可以在任何地方定义,在函数内或函数外,可以作为函数的参数和返回值,可以对函数进行组合。

    在JAVA8中引入的两个最主要的功能,lambda表达式以及stream,都需要基于这种思想来设计。

    比如:Java8中从函数式编程中引入的两个核心思想:将方法和Lambda作为一等值,以及在没有可变共享状态时,函数或方法可以有效、安全地并行执行。在我们stream api的设计中,都使用了这两种思想。

    后面我会总结出相关内容补上,我们继续来看。下一个

    行为参数化

    既然前面说了,JAVA8引入了函数式编程的思想的设计,那么如何实现呢?这就需要下面这个功能, 在java8中增加了一种把方法(你自己编写的代码)作为参数传递给另一个方法的能力。所以其实行为参数化就是方法参数化。在我们stream api的设计中也是利用这点:构建在通过传递代码使操作行为实现参数化的思想上的。只有把行为参数化,才可以把函数(或者说行为操作)作为参数传进函数中,进行编程吧。

    定义: 就是一个方法可以接受多种不同的行为作为参数,并且在内部使用他们,完成不同行为的能力。可以把一个行为(一段代码,结合匿名类)通过接口封装起来后在调用时候通过lambda方式实现,能够适应不断变化的需求,这种做法类似策略模式。

    下面我们还是引用java8实战这本书中的例子: 筛选苹果行为或者方式不同的例子(使用谓词)

    我先用一句话总结为什么需要下面的方式写哈,比如你产品经理有个需求,筛选10万个苹果中重量大于50千克的过滤出来。于是你草草写了个遍历、判断重量大于150k、返回结果。这时候客户又提了个需求,筛选10万个苹果中绿色的过滤出来。于是你又草草写了个遍历、判断重量大于150k、返回结果。写完之后,你有点不满意,感觉这代码都差不多啊,还很low,不符合我们减少重复代码原则吧,七七八八的,反正感觉很怪,这时候,产品经理又来了,过滤出红色苹果,小于100g的评估吧。。。于是你小脑筋一动,写了下面一行代码,小小难度。。

    public class AppleHeavyWeightPredicate implements ApplePredicate{
           public boolean test(Apple apple){
               return apple.getWeight() > 150;
           }
       }
       public class AppleGreenColorPredicate implements ApplePredicate{
           public boolean test(Apple apple){
               return "green".equals(apple.getColor());
           }
       }
       public class FilteringApples{
           public static void main(String...args){
               List<Apple> inventory = Arrays.asList(new Apple(80,"green"),
                       new Apple(155, "green"),
                       new Apple(120, "red"));
               List<Apple> heavyApples =
                       filterApples(inventory, new AppleHeavyWeightPredicate());
               List<Apple> greenApples =
                       filterApples(inventory, new AppleGreenColorPredicate());
           }
           public static List<Apple> filterApples(List<Apple> inventory,
                                                  ApplePredicate p) {
               List<Apple> result = new ArrayList<>();
               for (Apple apple : inventory){
                   if (p.test(apple)){
                       result.add(apple);
                   }
               }
               return result;
           }
       }
    
    这时候,你发现,虽然代码不冗余了,但是每次新的获取苹果的需求过来(其实我不爱吃苹果,为啥老是让我写苹果),你都要new一个类,实现接口,你发现好像代码实现起来不麻烦,写的很多。你想了下,搞个匿名类吧,于是有了下面一个实现:
    
    
    
    于是java8中提供了一个新的概念,叫方法引用的语法。
    你灵机一动,赶紧试了一下效果,哎,还不错哦:
    
    
    

    使用lambda表达式:

    List<Apple> result =
    filterApples(inventory, (Apple apple) -> "red".equals(apple.getColor()));
    

    方法引用

    方法引用是使用::语法(能够把这个方法作为"值",传递给方法中的参数,而不需要把该方法包含在一个对象中,在再该对象中使用该方法,使用比较简洁)。可以让你重复使用现有的方法实现并传递他们。

    lambda表达式=lambda函数=(匿名函数)

    一般我们都讲lambda表达式,可能有些童鞋看到这里会很奇怪,前面说的方法引用语法,其实就是将函数作为值的思想的使用,还包括了lambda(或者匿名函数)。

    我们可以理解为匿名函数,它没有名称,但有参数列表、函数主体、返回类型,可能还有一个可以抛出异常的列表。

    你可以这样写: (int x)-> x + 1 , 这是一个lambda表达式,表示"调用时,给定参数x,就返回x+1值的函数",在java8之前,我们要写一个工具类,然后类中包含一个add方法去调用并返回。但现在只需要一行代码就解决了这个问题了,这就是lambda表达式的第一个优点: 语法更简洁。

    在看一个例子:
    通常我们在程序开发时,经常需要遍历list,过滤一些条件,每次大概思路都是 : 遍历时判断条件去过滤:代码如下所示

    public class Apple {
    
        public Apple() {
        }
    
        public Apple(String color, int weight) {
            this.color = color;
            this.weight = weight;
        }
    
        private String color; // 颜色
        private int weight; // 重量
    
        //这里省略了color、weight的getter和setter方法。
        
        public static boolean filterRedApple(Apple apple) {
            return "red".equals(apple.getColor());
        }
    
        public static boolean filterWeightApple(Apple apple) {
            return apple.getWeight() > 150;
        }
    
        @Override
        public String toString() {
            return "Apple [color=" + color + ", weight=" + weight + "]";
        }
    }
    

    上面我们在过滤超重的苹果时,需要写一个方法,遍历时判断苹果的重量。
    当我们换了一种条件时,比如过滤红色的苹果计算苹果的总库存,又要写一个方法来去过滤。就会变得有很多重复的代码。

    我们可以使用Predicate :

    public static boolean isGreenApple(Apple apple) { 
        return "green".equals(apple.getColor());
    } 
    public static boolean isHeavyApple(Apple apple) { 
        return apple.getWeight() > 150;
    } 
    // 这部分可以不写,直接导入即可
    public interface Predicate<T>{
         boolean test(T t); 
    } 
    static List<Apple> filterApples(List<Apple> inventory, 
      Predicate<Apple> p) {
          List<Apple> result = new ArrayList<>(); 
          for (Apple apple: inventory){ 
              if (p.test(apple)) { 
                  result.add(apple); 
              } 
        } 
      return result; 
    } 
    要用它的话,你可以写:
    filterApples(inventory, Apple::isGreenApple); 
    或者
    filterApples(inventory, Apple::isHeavyApple);
    

    在java8中,把方法作为值来传递显然很有用,但要是为类似于isHeavyApple和isGreenApple这种可
    能只用一两次的短方法写一堆定义有点儿烦人。不过Java 8也解决了这个问题,它引入了一套新
    写法法(匿名函数或Lambda),让你可以写
    filterApples(inventory, (Apple a) -> "green".equals(a.getColor()) );
    或者
    filterApples(inventory, (Apple a) -> a.getWeight() > 150 );

    这里就引入了一个新的概念,匿名函数与函数式接口,关于函数式接口可以看我这篇文章:
    https://www.jianshu.com/p/6b1da190f11c

    行为参数化

    行为参数化是一个很有用的模式,它能够轻松地适应不断变化的需求。这
    种模式可以把一个行为(一段代码)封装起来,并通过传递和使用创建的行为(例如对Apple的不同谓词)将方法的行为参数化。前面提到过,这种做法类似于策略设计模式。

    相关文章

      网友评论

          本文标题:JAVA8新特性之不得不说的lambda表达式

          本文链接:https://www.haomeiwen.com/subject/urahiktx.html