轻松理解Lambda和方法引用

158 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

1、Lambda介绍

函数式编程,只关心函数!!!

函数式编程守则

  • 函数是“第一等公民”
  • 可以赋值给变量
  • 可以作为(其他函数的)参数进行传递
  • 可以作为(其他函数的)返回值

1.1 Lambda表达式的语法格式

image.png

1.2 语法格式细节

1.2.1 可选的大括号{}

解析:函数体只包含一个语句,不需要大括号

// 函数体只包含一个语句,省略大括号`String msg) -> System.out.println("hello" + msg);

1.2.2 可选的参数圆括号()

解析:只有一个参数,省略圆括号,同时省略类型

// 只有一个参数,省略圆括号,同时省略类型
msg -> System.out.println("hello "+ msg);

1.2.3 可选的返回关键字return

解析: 函数体只有一个表达式,且运算结果匹配返回类型,可以直接省略return

// 函数体只有一个表达式、省略return
(int a,int b) -> a+b 

1.2.4 可选的类型声明

解析: 不需要参数类型,编译器可以根据参数值进行推断

// 省略参数类型,编译器可以进行推断
(a,b) -> a+b; 

总结

  • 必须有一个函数式接口,有且只有一个抽象方法的接口 @Functionnallnterface 注解
  • 该接口:可以有静态方法,私有方法,Default,有且只有一个抽象方法

2、Lamdba表达式应用举例

常见的函数式接口:

  • Runnable/Callable
  • Supplier/Consumer
  • Comparator
  • Predicate
  • Function

2.1 Runnable接口

image.png

image.png

new Thread(()->{
    String name = Thread.currentThread().getName();
    System.out.println(name + "线程已启动");
}).start();

2.2 Supplier接口

Supplier 接口被称之为生产型接口,指定接口的泛型是什么类型,那么接口中的get方法就会产生什么类型的数据
image.png

public static void main(String[] args) {
    int arr[] = {2, 3, 4, 52, 333, 23};
    int max = getMax(() -> {
        int mx = arr[0];
        for (int i = 1; i < arr.length; i++) {
            mx = Math.max(mx, arr[i]);
        }
        return mx;
    });
    System.out.println(max);
}

public static int getMax(Supplier<Integer> supplier) {
    return supplier.get();
}

2.3 Consumer接口

accept: 这是Consumer功能接口的功能方法。accept方法对给定的参数进行这一操作。

andThen: 此方法返回一个组合的Consumer,该Consumer先执行原始的Consumer操作,然后按照从左到右的顺序执行给定的andThen操作。

image.png

public static void main(String[] args) {
    consumerString(s -> System.out.println(s));
    consumerString(s -> System.out.println(s.toUpperCase()),
            s -> System.out.println(s.toLowerCase())
    );
}

static void consumerString(Consumer<String> function) {
    function.accept("Hello");
}

static void consumerString(Consumer<String> first, Consumer<String> sec) {
    first.andThen(sec).accept("Hello");
}

2.4 Predicate接口

对某种数据类型进行判断,结果返回一个Boolean值

image.png

public static void main(String[] args) {
    andMethod(s->s.contains("W"),s->s.contains("H"));
    orMethod(s->s.contains("W"),s->s.contains("H"));
    negateMethod(s->s.length()<5);
}
static void andMethod(Predicate<String> one ,Predicate<String> two) {
    boolean isValid = one.and(two).test("Helloworld");
    System.out.println("字符串符合要求吗:" + isValid);
}
static void orMethod(Predicate<String> one ,Predicate<String> two) {
    boolean isValid = one.or(two).test("Helloworld");
    System.out.println("字符串符合要求吗:" + isValid);
}
static void negateMethod(Predicate<String> one ) {
    boolean isValid = one.negate().test("HelloWorld");
    System.out.println("字符串很长吗:" + isValid);
}

2.5 Function接口

Function接口接受一个T类型参数,返回R类型对象或值。

image.png

public static void main(String[] args) {
    method(s-> Integer.parseInt(s)+10,
            s->s*=10);
    String str = "周七,28";
    int age = getAgeNum(str,s -> s.split(",")[1],s->Integer.valueOf(s),i->i-=10);
    System.out.println(age);
}
static void method(Function<String,Integer> one,Function<Integer,Integer> two){
    int num = one.andThen(two).apply("10");
    System.out.println(num+20);
}
static int getAgeNum(String str,Function<String,String> one
                ,Function<String,Integer> two
                ,Function<Integer,Integer> three) {
    return one.andThen(two).andThen(three).apply(str);
}

3、方法引用

3.1 为什么需要方法引用?

当Lambda表达式所要完成的业务逻辑已经存在–已经有某个方法实现。我们可以直接引用对应的方法(函数),即方法引用。

3.2 方法引用语法格式

  • 双冒号::

3.3 哪些方法可以引用

image.png

4、方法引用应用举例

4.1 静态方法引用

保证引用方法与函数式接口抽象方法:参数列表相同,返回值类型相同

image.png

image.png

public static void main(String[] args) {
    // Math类存在静态abs方法,所以直接引用
    getNum(Math::abs);
}
static void getNum(Calcable calcable) {
    int num = calcable.calsAbs(-10);
    System.out.println(num);
}

4.2 构造方法引用

保证引用方法与函数式接口抽象方法:参数列表相同,返回值类型相同

image.png

image.png

public static void main(String[] args) {
    getName(Person::new);
}
static void getName(PersonBuilder personBuilder){
    System.out.println(personBuilder.builderPerson("kobe").getName());
}

4.3 普通方法引用

保证引用方法与函数式接口抽象方法:参数列表相同,返回值类型相同

image.png

image.png

public static void printString(Printable p) {
     p.print("HelloWorld");
}
public static void main(String[] args) {
	// 非静态方法,需要被对象引用
    ObjectMethodReference obj = new ObjectMethodReference();
    printString(obj::printUpperCaseString);
}

4.4 类方法引用(super)

保证引用方法与函数式接口抽象方法:参数列表相同,返回值类型相同

image.png

image.png

public class Man extends Human{
    @Override
    public void sayHello() {
        System.out.println("Hello 我是Man!");
    }
    // 定义一个方法参数传递Greetable接口
    public void method(Greetable g){
        g.greet();
    }

    /**
     * super::父类方法名
     */
    public void show(){
        method(super::sayHello);
    }
}

4.5 this方法引用

保证引用方法与函数式接口抽象方法:参数列表相同,返回值类型相同

image.png

image.png

public void buyHouse (){
    System.out.println("北京一套房");
}
public void marry(Richable r){
    r.buy();
}
public void soHappy(){
    marry(this::buyHouse);
}

4.6 数组的方法引用

# 接口
@FunctionalInterface
public interface ArrayBuilder {

    int[] builderArray(int length);
}




public static int[] createArray(int length,ArrayBuilder ab){
    return ab.builderArray(length);
}

public static void main(String[] args) {
    int[] arr1 = createArray(10,int[]::new);
    System.out.println(arr1.length);
}