Lambda表达式

Lambda表达式

Java Lambda表达式是Java 8中最重要的新特性之一。

是一种常用的函数对象表现形式,它们是一种可传递的匿名函数,可以作为参数传递给方法或存储在变量中,因此可以在需要的时候调用它们。

Lambda表达式的主要目的是简化Java代码,使其更易于阅读和编写。

表达形式

Lambda表达式的语法非常简洁和清晰。它们由参数列表、箭头符号和方法体组成。

  • 参数列表指定传递给Lambda表达式的参数
  • 箭头符号 “->” 分隔参数列表和方法体
  • 方法体则包含Lambda表达式要执行的代码。

例子:
(int a , int b) -> { int c = a + b; return c;}

这是常规的Lambda表达式写法

简写的特殊情况

  • 方法体只包含一行,则可以省略{}和return,代码多于一行,不能省略{}以及最后一行的return
    (int a , int b) -> a + b;

  • 只有一个参数则可以省略(),同时将类型声明去掉(加了类型声明就必须加上())
    a -> System.out.println(a)

  • 可以通过上下文推断出参数列表的类型,则无需另外声明参数类型
    (a, b) -> a + b;

Lambda1 lambda = (a, b) -> a + b;

interface Lambda1 {
	int op(int a, int b);
}

这里通过接口能够推断出参数类型,故使用时无需再声明参数类型

表述练习

1.判断语法正确性
在这里插入图片描述

  • 1.错误原因是因为两个参数需要加()
  • 2.正确,可以通过上下文推断出参数类型,可以不加参数类型
  • 3.错误,虽然可以通过上下文推断出参数类型,但是要么都加,要么都不加
  • 4.错误,一个参数可以省略(),同时也要去除类型声明,如果声明的参数类型,则需要加上()

函数式接口

可以将参数相同(个数和参数类型相同)以及返回值类型相同的函数统一管理

有且仅有一个抽象方法

1.自定义函数式接口

import java.math.BigInteger;
import java.util.ArrayList;

//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
public class Main {

    static class Student {
        String name;
        int age;
        public Student (){}
        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }
        public String getName() {
            return name;
        }

        public int getAge() {
            return age;
        }
    }
    public static void main(String[] args) {


        //obj1和obj2都是传入一个int参数,返回Boolean值,所以可以使用同一个函数式接口进行定义
        Type1 obj1 = (int a) -> (a & 1) == 1;
        Type1 obj2 = (int a) -> BigInteger.valueOf(a).isProbablePrime(100);


        Type2 obj3 = (int a, int b, int c) -> a + b + c;
        //obj4和obj5都是传入两个int参数,返回int值,所以也可以使用同一个函数式接口进行定义
        Type3 obj4 = (int a, int b) -> a * b;
        Type3 obj5 = (int a, int b) -> a - b;



        //Type4 obj6 = () -> new Student();
        //Type5 obj7 = () -> new ArrayList<Student>();

        //在定义函数式接口时,为了通用性,可以将两者的返回值设置为泛型
        Type6<Student> obj6 = () -> new Student();
        Type6<ArrayList<Student>> obj7 = () -> new ArrayList<Student>();

        //obj8和obj9二者的参数一样,但是返回类型不一样,为了通用性,可以将二者的函数式接口的参数和返回类型都设置为泛型,在定义时传入需要使用的类型包装类即可
        Type7<Student, String> obj8 = (Student s) -> s.getName();
        Type7<Student, Integer> obj9 = (Student s) -> s.getAge();
    }

    @FunctionalInterface
    interface Type1 {
        boolean op(int a);
    }

    @FunctionalInterface
    interface Type2 {
        int op(int a, int b, int c);
    }

    @FunctionalInterface
    interface Type3 {
        int op(int a, int b);
    }

    @FunctionalInterface
    interface Type4 {
        Student op();
    }

    @FunctionalInterface
    interface Type5 {
        ArrayList<Student> op();
    }

    @FunctionalInterface
    interface Type6<T> {
        T op();
    }

    @FunctionalInterface
    interface Type7<I, O> {
        O op(I input);
    }
}

其中参数类型也可以进一步省略(可以由函数式接口推断出参数类型)

2.JDK常见函数式接口

上述自定义的函数式接口在JDK中基本上都提供了,一般我们在编程时都是直接使用内置的函数式接口进行简化编程。
比如:Type7 对于Function接口

常见函数式接口
在这里插入图片描述
通过名字猜出函数式接口的参数以及返回值类型:
Consumer(消费者):消费,也就是传入之后被消费了,无返回值
IntConsumer:int参数类型
Predicate(条件):判断,返回值为Boolean
Bi:两参
Operator: 参数和返回值类型一样

常见名称含义在这里插入图片描述

应用例子:

1.在外部操作方法执行不同的规则

改变方法的形式,使得后续只需要在外部调用方法时传入不同的筛选规则则可以完成不同的筛选

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

public class Exercise3 {
    public static void main(String[] args) {
        
    }
    static List<Integer> filter(List<Integer> list) {
        List<Integer> result = new ArrayList<Integer>();
        for (Integer number : list) {
            //筛选条件:判断是否为偶数
            //后面希望在不改变该方法的前提下,在外部通过调用该方法,传入不同筛选规则来改变方法的执行结果
            if ((number & 1) == 0) {
                result.add(number);
            }
        }
        return result;

        /*
        1. 将筛选条件提取出来:
            (number & 1) == 0
        2. 该语句只是执行体,也就是方法体,我们需要知道传入什么参数才能执行
            (Integer number) -> (number & 1) == 0
        3. 通过该lambda表达式找到对应的函数式接口: 传入一个参数,返回Boolean类型  =》Predicate
            改写该方法
         */
    }
}

改写之后:

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

public class Exercise3 {
    public static void main(String[] args) {
        List<Integer> result = filter(List.of(1, 2, 3, 4, 5, 6), (Integer number) -> (number & 1) == 0);
        System.out.println(result);
    }
    static List<Integer> filter(List<Integer> list, Predicate<Integer> predicate) {
        List<Integer> result = new ArrayList<Integer>();
        for (Integer number : list) {
            //筛选条件:判断是否为偶数
            //后面希望在不改变该方法的前提下,在外部通过调用该方法,传入不同筛选规则来改变方法的执行结果
            if (predicate.test(number)) {
                result.add(number);
            }
        }
        return result;

        /*
        1. 将筛选条件提取出来:
            (number & 1) == 0
        2. 该语句只是执行体,也就是方法体,我们需要知道传入什么参数才能执行
            (Integer number) -> (number & 1) == 0
        3. 通过该lambda表达式找到对应的函数式接口: 传入一个参数,返回Boolean类型  -> Predicate
            改写该方法,
        4. 方法调用
         */
    }
}

解释:

(Integer number) -> (number & 1) == 0)作为一个函数对象,也就是Predicate接口的实现:Predicate<Integer> predicate传入filter方法中

函数对象中逻辑 (number & 1) == 0的执行:

  • 通过Predicate函数接口中的test方法就可以执行函数对象中的逻辑: predicate.test(number)

后面我们只需要在调用方法时,传入不同的函数对象,就能根据不同的规则来改变执行结果,而不需要修改方法

比如:
现在的筛选条件: (number & 1) == 0) 是筛选偶数
后面只需要简单修改: (number & 1) == 1) 就变为筛选奇数

2.在外部操作方法实现不同的字符串操作

改变方法的形式,使得后续只需要在外部调用方法时传入不同的字符串操作可以完成不同的字符串处理

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;

public class Exercise3 {
    public static void main(String[] args) {
        List<String> result1 = map(List.of(1, 2, 3, 4, 5), (Integer number) -> String.valueOf(number));
        System.out.println(result1);
    }
    static List<String> map(List<Integer> list, Function<Integer, String> function) {
        List<String> result = new ArrayList<>();
        for (Integer number : list) {
            //转换:将数字转为字符串,但以后可能改变转换规则
            result.add(function.apply(number));
        }
        return result;

        /*
        1. 将执行的主逻辑提取出来
            String.valueOf(number)
        2. 补全函数对象
            (Integer number) -> String.valueOf(number);
        3. 找到对应的函数式接口
             一个参数,一个返回值: Function
        4. 方法中传入Function的对象,实际调用方法时将Lambda表达式作为函数对象传入方法,从而调用对应接口实现的抽象方法完成逻辑的执行
         */
    }
}

解释:

(Integer number) -> String.valueOf(number) + 1作为一个函数对象,也就是Function接口的实现:Function<Integer, String> funtion传入map方法中

方法中传入Function的对象,实际调用方法时将Lambda表达式作为函数对象传入方法,从而调用Function接口的抽象方法完成逻辑的执行

比如:
现在的字符串处理是:(Integer number) -> String.valueOf(number) 是单纯将提供的数字转成字符串
后面只需要简单修改: (Integer number) -> String.valueOf(number) + 1 就变为先将提供的数字转成字符串,然后在其后面拼接一个字符1,比如:2 -> "2" + "1" = "21"

3.传入参数无返回值的操作修改

改变方法的形式,使得后续只需要在外部方法时,只需要传入不同的消费规则就能实现对应的操作

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;

public class Exercise3 {
    public static void main(String[] args) {
        consume(List.of(1, 2, 3, 4, 5), (Integer number) -> System.out.println(number));
    }
    static void consume(List<Integer> list, Consumer<Integer> consumer) {
        for (Integer number : list) {
            //消费: 打印,但是后面可能会改变消费规则
            consumer.accept(number);
        }
    }
}

现在的处理是:(Integer number) -> System.out.println(number)) 是单纯将提供的数字打印出来
后面只需要简单修改: (Integer number) -> System.out.println(number * 2)) 就变为打印提供数字的平方

现在看上去是有些脱裤子放屁,但是后面深入的话,该模式还是有很复杂的用处,请务必好好理解

上述内容是学习过程中摘录的片段,不作为商业用途,如有侵权,联系删除。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值