Java8新特性1:lambda表达式入门--由浅入深,从单发步枪迈向自动步枪

本文主要参照《java8 in action》书中对lambda的讲解,来循序渐进的引入lambda表达式,了解我们为什么,以及怎么样初步学会使用lambda表达式,看完以后你会恍然大悟,不是为了用lambda,而用lambda。

点击获取:《java8 in action》英文原版pdf

写在前面:

在熟悉lambda表达式和方法引用(双冒号操作,后面一篇博文讲)后,我们将能够更加简洁的完成需求编码。

例如:按照苹果颜色完成对一个苹果集的排序:

        //1、传统的已经够简化的匿名内部类方式
        list.sort(new Comparator<Apple>() {
            @Override
            public int compare(Apple o1, Apple o2) {
                return o1.getColor().compareTo(o2.getColor());
            }
        });
        
        //2、使用lambda进一步简化
        list.sort((o1,o2)->o1.getColor().compareTo(o2.getColor()));
        
        //2、使用方法引用终极简化
        list.sort(Comparator.comparing(Apple::getColor));

上面只是一个很肤浅但直观的一种对lambda和方法引用的一种展现,详细的使用和原理学习还得往下看。

一、为什么使用lambda表达式?

现在我们通过《java8 in action》书中Chapter 2部分的2.1. Coping with changing requirements章节,作者提出的一个问题(如何处理多变的需求)来由浅入深的解释为什么要是用lambda。他给了需求背景:

 大概意思就是说,现在你需要做一个农场库存管理的应用程序,农场主的有各种各样的处于变化的需求,你如更好的实现这些可能出现变化的需求,下面详细展开。

1、文中首先提出了一个最基础的需求1::在仓库里的众多苹果中,找出颜色为绿色的苹果

要实现这个需求,我们需要以下代码,:

  • 1.1 Apple.java  这是一个苹果的bean,我实现了它的构造方法和to String方法(后面的需求举例皆需要用到这个bean):
package com.aigov.java8_newfeatures.lambda;

import lombok.Data;

/**
 * @author : aigoV
 * @date :2019/10/10
 * apple
 **/
@Data
public class Apple {
    private String color;
    private long weight;

    public Apple(String color, long weight) {
        this.color = color;
        this.weight = weight;
    }

    @Override
    public String toString() {
        return "Apple{" +
                "color='" + color + '\'' +
                ", weight='" + weight + '\'' +
                '}';
    }
}
  • 1.2同时我们需要创建一个FilterApple.java 过滤器,我把所有实现的相关代码全写在这里面(之后的需求实现也在这个类里,只不过会去掉之前的代码):
package com.aigov.java8_newfeatures.lambda;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author : aigoV
 * @date :2019/10/10
 * apple过滤器
 * 根据农场主的特定需求找出特定苹果
 **/
public class FilterApple {

    /** 需求1::在众多苹果中,找出颜色为绿色的苹果**/
    public static List<Apple> findGreenApple(List<Apple> appleList){

        List<Apple> list = new ArrayList<>();
        for (Apple apple : appleList){
            if ("green".equals(apple.getColor())){
                list.add(apple);
            }
        }
        return list;
    }

    
    public static void main(String[] args) {

        /** 苹果库存集 **/
        List<Apple> list = Arrays.asList(
          new Apple("green",150),
          new Apple("yellow",120),
          new Apple("green",170)
        );

        //
        List<Apple> greenApples = findGreenApple(list);
        assert greenApples.size() == 2:"过滤后的集合size与预期不符"; //java assert(断言),基本的测试作用,须在idea设置启用。
        System.out.print(greenApples);
        

}

到这,这个简单的需求1就实现了。

2、需求2:需要应对随时变化的需求,比如:不找绿色了,找红色的苹果

这个需求比需求1要求多一点,但其实也很简单,想必大家想的和作者提供的方案一样(当然你也可能想的是再多加一个单独的找红色的方法,,),在过滤器的方法中加入一个color参数,就可以动态满足农场主的需求,代码见下:

package com.aigov.java8_newfeatures.lambda;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author : aigoV
 * @date :2019/10/10
 * apple过滤器
 * 根据农场主的特定需求找出特定苹果
 **/
public class FilterApple {

    /** 需求2:需要应对随时变化的需求,比如:不找绿色了,找红色的苹果**/
    public static List<Apple> findApple(List<Apple> appleList,String color){

        List<Apple> list = new ArrayList<>();
        for (Apple apple : appleList){
            if (color.equals(apple.getColor())){
                list.add(apple);
            }
        }
        return list;
    }

    
    public static void main(String[] args) {

        /** 苹果库存集 **/
        List<Apple> list = Arrays.asList(
          new Apple("green",150),
          new Apple("yellow",120),
          new Apple("green",170)
        );

        //
        List<Apple> redApples = findApple(list,"red");
        System.out.print(redApples);
        

}

3、需求3:不只是按颜色来找,需要按照重量,颜色去找(也许apple还会有其他属性,需求还会更多变,更复杂,总之你需要满足不停变化的各种需求)

在这里《java8 in action》的作者给我们提供一种解决方案,那就是使用策略模式去应对复杂多变的需求。代码实现:

package com.aigov.java8_newfeatures.lambda;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author : aigoV
 * @date :2019/10/10
 * apple过滤器
 * 根据农场主的特定需求找出特定苹果
 **/
public class FilterApple {

    /** 需求3:不只是按颜色来找,需要按照重量,颜色去找(你需要满足不停变化的各种需求)**/
    //这是抽象出来的过滤接口
    public interface AppleFilter{
        boolean filter(Apple apple);
    }

    //这是一个过滤方法
    public static List<Apple> findApple(List<Apple> appleList,AppleFilter appleFilter){
        List<Apple> list = new ArrayList<>();
        for (Apple apple : appleList){
            if (appleFilter.filter(apple)){
                list.add(apple);
            }
        }
        return list;
    }
    //按照上面策略模式接口,现在需求是找到颜色为绿色 并且重量>=150的苹果 (这是具体策略的策略类,不同需求建不同策略类)
    public static class GreenAnd160weiFilter implements AppleFilter{
        @Override
        public boolean filter(Apple apple) {

                return "green".equals(apple.getColor()) && apple.getWeight()>=160;
        }
    }

    //注意:这里你可能需要为多种多样的需求增添多种多样的策略类,这里就暂写上面那一个

    
    public static void main(String[] args) {

        /** 苹果库存集 **/
        List<Apple> list = Arrays.asList(
          new Apple("green",150),
          new Apple("yellow",120),
          new Apple("green",170)
        );

        //
        List<Apple> redApples2 = findApple(list,new GreenAnd160weiFilter());
        //System.out.print(redApples2);
        

}

4、使用内部类简化策略模式

上面三个由简至繁的需求,实际上使我们在实际开发中的一个缩影,需求总是在变,后台方法也在随之而变,在上面的例子中,我们发现使用策略模式其实已经就可以满足复杂多变的需求了,但是作者觉得这样写代码,还不够简洁,使用策略模式,我们可能需要建很多策略类,这似乎很繁琐。

在此情况下作者又给我们多了一个思路:内部类。上面需求3的策略模式实现代码可以改为以下:

package com.aigov.java8_newfeatures.lambda;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author : aigoV
 * @date :2019/10/10
 * apple过滤器
 * 根据农场主的特定需求找出特定苹果
 **/
public class FilterApple {

    /** 需求3:不只是按颜色来找,需要按照重量,颜色去找(你需要满足不停变化的各种需求)**/
    //这是抽象出来的过滤接口
    public interface AppleFilter{
        boolean filter(Apple apple);
    }

    //这是一个过滤方法
    public static List<Apple> findApple(List<Apple> appleList,AppleFilter appleFilter){
        List<Apple> list = new ArrayList<>();
        for (Apple apple : appleList){
            if (appleFilter.filter(apple)){
                list.add(apple);
            }
        }
        return list;
    }
    

    

    
    public static void main(String[] args) {

        /** 苹果库存集 **/
        List<Apple> list = Arrays.asList(
          new Apple("green",150),
          new Apple("yellow",120),
          new Apple("green",170)
        );

        //
        /** 使用匿名内部类处理多变需求 (实际上就是将需求3的策略类用匿名类直接表示)**/
        List<Apple> yellowApple = findApple(list,new AppleFilter(){
            @Override
            public boolean filter(Apple apple) {
                return "yellow".equals(apple.getColor()) && apple.getWeight()==120;
            }
        });

        //注意:这里你可能需要为多种多样的需求增添多种多样的上面这种匿名类,这里就暂写上面那一个
        System.out.print(yellowApple);
        

}

好了,匿名内部类的使用,我们会发现我们确实不用再去新建很多策略类了,但是不可避免的,我们仍然要为多变的需求去写很多匿名类,并且匿名类的代码也还是不够简洁,而且在书中,作者提到,使用匿名内部类,程序员有时会对内部类里面的成员参数调用产生误解(应该不会吧,,):

好了到这里,我们已经了解到了需求是多变的这个事实,也清楚了在没有lambda之前,我们一般是怎么处理他的,当然从上面例子想必大家也知道了不用lambda或者说以前这些实现方式的一些弊端:

  • 代码繁杂
  • 作者特意指出的内部类的弊端
  • 其实还有个原因:java8 中使用lambda会相对优化内存

二、如何使用lambda表达式?

1、现在我们就用lambda表达式来解决上面的需求3的场景,我们先看代码实现,后面解释lambda表达式是如何使用的:

package com.aigov.java8_newfeatures.lambda;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author : aigoV
 * @date :2019/10/10
 * apple过滤器
 * 根据农场主的特定需求找出特定苹果
 **/
public class FilterApple {

    /** 需求3:不只是按颜色来找,需要按照重量,颜色去找(你需要满足不停变化的各种需求)**/
    //这是抽象出来的过滤接口

    /** java8中,如果你的接口只有一个方法(default static方法除外),那么可以用这个注解,也就是你这个接口可以用lambda表达式 **/
    @FunctionalInterface
    public interface AppleFilter{
        boolean filter(Apple apple);
    }

    //这是一个过滤方法
    public static List<Apple> findApple(List<Apple> appleList,AppleFilter appleFilter){
        List<Apple> list = new ArrayList<>();
        for (Apple apple : appleList){
            if (appleFilter.filter(apple)){
                list.add(apple);
            }
        }
        return list;
    }
    
    
    public static void main(String[] args) {

        /** 苹果库存集 **/
        List<Apple> list = Arrays.asList(
          new Apple("green",150),
          new Apple("yellow",120),
          new Apple("green",170)
        );

        //
        List<Apple> yellowApple2 = findApple(list,(Apple apple)->{
            return "yellow".equals(apple.getColor()) && apple.getWeight()==120;
        });
        System.out.print(yellowApple2);
}

2、解释lambda表达式具体如何使用

  • 2.1这是对上面lambda表达式语法的标注,看了之后应该就已经一目了然了:

 对于上面这个,我们可以简化成下面这样:

  • 2.1 要使用lambda表达式,那么你自己写的对应接口应该加上 @FunctionalInterface注解,这个注解的使用见下面:

实际上java8源码包下面包含了许多被@FunctionalInterface注解的接口,他们都可以被lambda表达式服务:

比如上图中打开的 接口中的方法,他就可以写出以下的lambda表达式:

Function<String,Integer > f = s -> s.length();

它表示给一个String类型的参数,会返回一个Integer类型的返回值。 

三、举个栗子

现在让我们加深一下印象:来看看不用lambda和使用lambda构建一个线程的区别:

 

 

 

java8 新特性解析(更新中):

Java8新特性1:lambda表达式入门--由浅入深,从单发步枪迈向自动步枪 

Java8新特性2:方法引用--深入理解双冒号::的使用

Java8新特性3:Stream1——什么是Stream,Stream的特性,如何使用Stream,Stream与Collection集合的区别

Java8新特性3:Stream2—一文详解Stream API,让你快速理解Stream Api提供的诸多常用方法

Java8新特性3:Stream3—数值流与对象流的转化及其方法使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值