《Java8新特性》- lambda

《Java8新特性》- lambda

Scroll Down

1. Lambda 入门

1.1 lambda表达式简介

  • Lambda表达式是一个匿名函数, 我们可以把Lambda表 达式理解为是一段可以传递的代码(将代码像数据一样 进行传递) 。可以写出更简洁、更灵活的代码。作为一
    种更紧凑的代码风格,使得Java语言表达能力得到了提升。JDK8 中引入了一个新的操作符" -> "该操作符称为箭头操作符或Lambda操作符,箭头操作符将Lambda表
    达式拆分为两部分:
  • 左侧:Lambda表达式的参数列表。对应接口中抽象方法的参数列表。
  • 右侧:Lambda表达式中所需要执行的功能,即Lambda
    体。对应接口中抽象方法的实现

1.2 lambda基本使用

  • 接口
public interface Bird {
    void fly(Integer times);
}

  • 三种实现方法
  • 注意 : Lambda表达式需要函数式接口的支持, 函数式接口就是: 接口中有且仅有一个抽象方法的接口
package cn.knightzz.work;

import cn.knightzz.service.Bird;

public class LambdaTest {

    public static void main(String[] args) {
        Bird bird01 = new BirdImpl();
        bird01.fly(1000);

        // 实现接口的方式二: 匿名内部类
        Bird bird02 = new Bird() {
            @Override
            public void fly(Integer times) {
                System.out.println("方式二 : 小鸟飞行了 : " + times + "s");
            }
        };
        bird02.fly(1000);

        // 方式三: lambda表达式 : (参数1,参数2,...) -> {}
        // TODO:Lambda表达式需要函数式接口的支持, 函数式接口就是: 接口中有且仅有一个抽象方法的接口
        Bird bird03 = (param) -> {
            System.out.println("方式三 : 小鸟飞行了 : " + param + "s");
        };
        bird03.fly(1000);
    }
}

/**
 * 实现接口的方式一
 */
class BirdImpl implements Bird {

    /**
     * 飞行
     *
     * @param times 飞行时间
     */
    @Override
    public void fly(Integer times) {
        System.out.println("方式一: 小鸟飞行了 : " + times + "s");
    }
}

2. Lambda 基本语法

  • 我们可以使用 lambda 语法代替匿名方法的创建, Lambda语法如下

  • 如果 lambda 方法体中只有一行代码, {} 可以省略

    (参数1,参数2,...) -> {}
    
  • 注意:Lambda表达式需要函数式接口的支持

2.1 函数式接口

  • 接口中有且仅有一个抽象方法(必须被重写的方法)的接口,称为函数式接口。 注意是必须被重写的方法!!!!, 默认方法不是必须被重写的,但是我们也可以重写它!
  • default 修饰方法只能在接口中使用(只能被实现了这个接口的对象调用),在接口种被 default 标记的方
    法为普通方法,可以直接写方法体。
  • 可以使用注解 @FunctionalInterface 修饰,可以检查是否是函数式接口。如果不是函数型接口会报错
@FunctionalInterface
public interface Dog {
    /**
     * 狗叫
     *
     * @param dogName 狗的名字
     */
    void yap(String dogName);

    // void flaw(); // 非函数式接口会报错

    /**
     * 默认方法, 实现 Dog 接口, 不是必须需要实现该方法
     */
    default void fly() {
        System.out.println("默认方法 fly() ");
    }

    /**
     * 默认方法,可以定义多个
     */
    default void flaw() {
        System.out.println("默认方法 flaw() ");
    }
}

  • Lambda表达式需要函数式接口的支持以前我们整个 java 的继承关系已经使用了很多年,如果在 java 新版本中为了迎合函数式接口一个接口中只有一个抽象方法,会导致以前很多接口失效,那么别人也没法进行 jdk 的升级所以提供了默认方法,在 jdk1.8 之后,可以在接口中定义默认方法.
  • 注意函数式接口中只能有一个是必须被重写的方法!!!!, 默认方法不是必须被重写的,但是我们也可以重写它!
  • 所以, 我们可以给其他接口上添加 default , lambda只需要实现函数式接口
class BigDog implements Dog {

    /**
     * 函数式接口
     *
     * @param dogName 狗的名字
     */
    @Override
    public void yap(String dogName) {
        System.out.println("BigDog name is : " + dogName);
    }
	
    // 实现的都是默认接口
    @Override
    public void flaw() {
        System.out.println("一只bigDog在抓");
    }
	
    // 实现的都是默认接口
    @Override
    public void fly() {
        System.out.println("一只bigDog在飞");
    }
}

  • 一些固定的接口我们可以设置为默认接口并实现他, 需要根据具体业务变化的接口我们可以定义成函数式接口, 动态的实现具体的逻辑
public class FunctionInterfaceTest {

    public static void main(String[] args) {

        Dog dog = (name) -> {
            System.out.println("dog name is : " + name);
        };

        dog.yap("cat");
        dog.flaw();
        dog.fly();

        // 这个方法可以根据实际业务需求来重写
        Dog dog02 = (name) -> {
            System.out.println("dog name is : " + name);
        };

        dog02.yap("cat");
        BigDog bigDog = new BigDog();
        bigDog.flaw();
        bigDog.fly();
        bigDog.yap("pig");
    }
}

3. 内置函数式接口

Lambda的实现依赖于函数式接口,接口可以我们自己定义,当然也可以使用JDK已经提供好的一些函数式
接口,这些接口基本已经能够满足我们的常用操作,并且在集合等框架中已经广泛地使用了,所以我们可以直接使用这些接口。

  • 消费型、供给型、函数型、断定型
    • 消费型接口 Comsumer void accept(T t)
    • 供给型接口 Supplier T get()
    • 函数型接口 Function<T,R> R apply(T t)
    • 断定型接口 Predicate boolean test(T t)
  • 消费性:只进不出
    供给型:白手起家,空手套白狼
    函数型:礼尚往来
    断定行:鉴定评审
  • 注意:关注的只有接口中方法的参数和返回值即可

3.1 Consumer 消费型接口

带一个参数,在方法体中使用完了就完了,例如在Collection中的 forEach 方法则需要一个Consumer接口的实现类对象。

public class CollectionInterface {

    public static void main(String[] args) {
        // 消费型接口
        Collection<String> list = new ArrayList<>();
        list.add("aaa");
        list.add("aaa2");
        list.add("aaa3");
        list.add("aaa4");
        // forEach(Consumer<? super T> action)
        list.forEach(item -> System.out.println(item));
    }
}

3.2 Supplier 供给型接口

public class SupplierInterface {

    public static void main(String[] args) {

        SupplierInterface supplierInterface = new SupplierInterface();
        List<Integer> numberList = supplierInterface.getNumberList(10, () -> (int) (Math.random() * 100));
        numberList.forEach(item -> System.out.println(item));
    }

    public List<Integer> getNumberList(int num, Supplier<Integer> data) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            Integer n = data.get();
            list.add(n);
        }
        return list;
    }
}

3.3 Function <T, R> 函数型接口

  • 函数通过 apply(str) 调用我们自己编写的业务逻辑

  • 自定义 apply

     String result = functionalInterface.strHandler("abc", (str) -> {
                // 注意: 这里的 str 就是 function.apply(str) 的 str
                return str.toUpperCase();
    });
    
  • 核心代码

package cn.knightzz.inter;

import java.util.function.Function;

/**
 * @author knightzz98
 * @title: FunctionInterface
 * @projectName JavaBase
 * @description:
 * @date 2021/9/4 18:29
 */
public class FunctionInterface {

    public static void main(String[] args) {

        FunctionInterface functionalInterface = new FunctionInterface();

        String result = functionalInterface.strHandler("abc", (str) -> {
            // 注意: 这里的 str 就是 function.apply(str) 的 str
            return str.toUpperCase();
        });

        System.out.println("result = " + result);

    }

    /**
     * 处理字符串
     * Function<String, String> : 第一个是 apply(param) 参数值类型, 第二个是返回值类型
     *
     * @return 处理完成的字符串
     */
    public String strHandler(String str, Function<String, String> function) {
        // 这里可以写统一处理逻辑
        return function.apply(str);
    }
}

3.4 Predicate 断言型接口

  • predicate.test(data) 返回值为 boolean 类型

  • 我们自己编写的代码就相当于自定义 test(data) 方法

    List<String> strs = pre.filterStr(Arrays.asList(datas), (data) -> {
                // 编写 predicate.test(data) 方法的逻辑, 返回值为 boolean
                return data.length() > 2;
            });
    
  • 核心代码

package cn.knightzz.inter;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.Predicate;

/**
 * @author knightzz98
 * @title: PredicateInterface
 * @projectName JavaBase
 * @description: 断言型接口
 * @date 2021/9/4 18:37
 */
public class PredicateInterface {

    public static void main(String[] args) {
        PredicateInterface pre = new PredicateInterface();
        String[] datas = new String[]{"aaaa", "bb", "ccc"};
        List<String> strs = pre.filterStr(Arrays.asList(datas), (data) -> {
            // 编写 predicate.test(data) 方法的逻辑, 返回值为 boolean
            return data.length() > 2;
        });
        strs.forEach(item -> System.out.println(item));
    }

    public List<String> filterStr(List<String> datas, Predicate<String> predicate) {
        List<String> list = new ArrayList<>();
        for (String data : datas) {
            if (predicate.test(data)) {
                list.add(data);
            }
        }
        return list;
    }
}

4. 方法引用

简单看了下, 性能没提升, 代码可读性变差, 除了代码短了, 没别的用处, 预测用了肯定会挨骂, 暂时定义一个目录, 用到了再学

4.1. 对象::实例方法名

4.2. 类::静态方法名

4.3. 类::实例方法名

4.4. 构造器引用