当前位置: 首页 > news >正文

每天学一点之Lambda表达式

Lambda表达式

思想导入:

函数式编程思想: 在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿什么东西做什么事情”。编程中的函数,也有类似的概念,你调用我的时候,给我实参为形参赋值,然后通过运行方法体,给你返回一个结果。对于调用者来做,关注这个方法具备什么样的功能。相对而言,面向对象过分强调“必须通过对象的形式来做事情”,而函数式思想则尽量忽略面向对象的复杂语法——强调做什么,而不是谁来做

 @Test
    public void test1(){
        String[] arr = {"hello","java","hi","xiao","yu"};
        //把上面的字符串按照长短排序,从短到长
//        Arrays.sort(arr);//按照编码排序
        //public static <T> void sort(T[] a, Comparator<? super T> c)
        Arrays.sort(arr, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
//                return o1.length()-o2.length();
                return Integer.compare(o1.length(), o2.length());
            }
        });
        /*
        上面的写法使用了匿名内部类,既声明了一个类,又创建了一个对象。
        创建这个对象的目的是为了给sort方法的第二个形参c赋值。
        声明匿名内部类的目的是为了重写public int compare(String o1, String o2)
         */
        System.out.println(Arrays.toString(arr));
    }
  //这个需求中我们关心的是什么? 如何比较两个字符串的大小,至于对象不重要
        Arrays.sort(arr, (o1, o2) -> Integer.compare(o1.length(), o2.length()));
        System.out.println(Arrays.toString(arr));

做什么,而不是谁来做,怎么做

我们真的希望创建一个匿名内部类对象吗?不。我们只是为了做这件事情而不得不创建一个对象。我们真正希望做的事情是:将compareTo方法体内的代码传递给sort方法知晓。

传递一段代码——这才是我们真正的目的。而创建对象只是受限于面向对象语法而不得不采取的一种手段方式。使用Lambda表达式不再有“不得不创建接口对象”的束缚,就是这么简单!

一、函数接口的概念

Lambda表达式其实就是实现SAM接口的语法糖,所谓SAM接口就是Single Abstract Method,即该接口中只有一个抽象方法需要实现,当然该接口可以包含其他非抽象方法(可以包含默认方法,静态方法,也就是实现此接口的子类只要求实现一个方法)。只要满足“SAM”特征的接口都可以称为函数式接口,都可以使用Lambda表达式,但是如果要更明确一点,最好在声明接口时,加上 @FunctionalInterface. 一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。

虽然Iterable和Comparable接口也只有一个抽象方法,但它们没有@FunctionalInterface注解标记,因此不算。

序号接口抽象方法SAM接口
1java.lang.Runnablepublic void run()
2java.util.Comparatorpublic int compare(T t1, T t2)
3java.io.FileFilterpublic boolean accept(File pathname)
4java.io.FilenameFilterpublic boolean accept(File dir, String name)

Lambda表达式语法格式

(形参列表) -> {Lambda体}

Lambda表达式的简化

  • 当{Lambda体}中只有一句语句时,可以省略{}和{;}
  • 当{Lambda体}中只有一句语句时,并且这个语句还是一个return语句,那么{、return、;}三者可以省略。它们三要么一起省略,要么都不省略。
  • 当Lambda表达式(形参列表)的类型已知,获取根据泛型规则可以自动推断,那么(形参列表)的数据类型可以省略。
  • 当Lambda表达式(形参列表)的形参个数只有一个,并且类型已知或可以自动推断,则形参的数据类型和()可以一起省略,但是形参名不能省略。
  • 当Lambda表达式(形参列表)是空参时,()不能省略

二、Java8之后引入的函数式接口

Java8在java.util.function新增了很多函数式接口:主要分为四大类,消费型、供给型、判断型、功能型。

1、消费型接口
有形参,但是返回值类型是void

序号接口名抽象方法描述
1Consumervoid accept(T t)接收一个对象用于完成功能
2BiConsumer<T,U>void accept(T t, U u)接收两个对象用于完成功能
3DoubleConsumervoid accept(double value)接收一个double值
4IntConsumervoid accept(int value)接收一个int值
5LongConsumervoid accept(long value)接收一个long值
6ObjDoubleConsumervoid accept(T t, double value)接收一个对象和一个double值
7ObjIntConsumervoid accept(T t, int value)接收一个对象和一个int值
8ObjLongConsumervoid accept(T t, long value)接收一个对象和一个long值

常用的方法:

public default void forEach(Consumer<? super T> action) 该方法功能是遍历Collection集合,并将传递给action参数的操作代码应用在每一个元素上。

 List<String> list = Arrays.asList("java","c","python","c++","VB","C#");
        list.forEach(s -> System.out.println(s));

2、供给型接口
无参,但是有返回值

序号接口名抽象方法描述
1SupplierT get()返回一个对象
2BooleanSupplierboolean getAsBoolean()返回一个boolean值
3DoubleSupplierdouble getAsDouble()返回一个double值
4IntSupplierint getAsInt()返回一个int值
5LongSupplierlong getAsLong()返回一个long值

3、判断型接口
有参,但是返回值类型是boolean结果。

序号接口名抽象方法描述
1Predicateboolean test(T t)接收一个对象
2BiPredicate<T,U>boolean test(T t, U u)接收两个对象
3DoublePredicateboolean test(double value)接收一个double值
4IntPredicateboolean test(int value)接收一个int值
5LongPredicateboolean test(long value)接收一个long值

常用的方法

public default boolean removeIf(Predicate<? super E> filter) 用于删除集合中满足filter指定的条件判断的。

   //删除包含o字母的元素
        list.removeIf(s -> s.contains("o"));

4、功能型接口
既有参数又有返回值

序号接口名抽象方法描述
1Function<T,R>R apply(T t)接收一个T类型对象,返回一个R类型对象结果
2UnaryOperatorT apply(T t)接收一个T类型对象,返回一个T类型对象结果
3DoubleFunctionR apply(double value)接收一个double值,返回一个R类型对象
4IntFunctionR apply(int value)接收一个int值,返回一个R类型对象
5LongFunctionR apply(long value)接收一个long值,返回一个R类型对象
6ToDoubleFunctiondouble applyAsDouble(T value)接收一个T类型对象,返回一个double
7ToIntFunctionint applyAsInt(T value)接收一个T类型对象,返回一个int
8ToLongFunctionlong applyAsLong(T value)接收一个T类型对象,返回一个long
9DoubleToIntFunctionint applyAsInt(double value)接收一个double值,返回一个int结果
10DoubleToLongFunctionlong applyAsLong(double value)接收一个double值,返回一个long结果
11IntToDoubleFunctiondouble applyAsDouble(int value)接收一个int值,返回一个double结果
12IntToLongFunctionlong applyAsLong(int value)接收一个int值,返回一个long结果
13LongToDoubleFunctiondouble applyAsDouble(long value)接收一个long值,返回一个double结果
14LongToIntFunctionint applyAsInt(long value)接收一个long值,返回一个int结果
15DoubleUnaryOperatordouble applyAsDouble(double operand)接收一个double值,返回一个double
16IntUnaryOperatorint applyAsInt(int operand)接收一个int值,返回一个int结果
17LongUnaryOperatorlong applyAsLong(long operand)接收一个long值,返回一个long结果
18BiFunction<T,U,R>R apply(T t, U u)接收一个T类型和一个U类型对象,返回一个R类型对象结果
19BinaryOperatorT apply(T t, T u)接收两个T类型对象,返回一个T类型对象结果
20ToDoubleBiFunction<T,U>double applyAsDouble(T t, U u)接收一个T类型和一个U类型对象,返回一个double
21ToIntBiFunction<T,U>int applyAsInt(T t, U u)接收一个T类型和一个U类型对象,返回一个int
22ToLongBiFunction<T,U>long applyAsLong(T t, U u)接收一个T类型和一个U类型对象,返回一个long
23DoubleBinaryOperatordouble applyAsDouble(double left, double right)接收两个double值,返回一个double结果
24IntBinaryOperatorint applyAsInt(int left, int right)接收两个int值,返回一个int结果
25LongBinaryOperatorlong applyAsLong(long left, long right)接收两个long值,返回一个long结果

常用的方法

default void replaceAll(UnaryOperator operator)将该列表的每个元素替换为将该运算符应用于该元素的结果。

 //使用Lambda表达式实现Function<T,R>接口的子接口UnaryOperator<T>,
        // 可以实现将一个字符串首字母转为大写的功能。
        list.replaceAll(s -> s.substring(0,1).toUpperCase() + s.substring(1));

5、自定义函数式接口
只要确保接口中有且仅有一个抽象方法即可:

修饰符 interface 接口名称 {
public abstract 返回值类型 方法名称(可选参数信息);
// 其他非抽象方法内容
}

接口当中抽象方法的 public abstract 是可以省略的

案例:

  • 声明一个转换器Convertor<T,R>,包含抽象方法change,可以将参数转换为另一个值,并返回结果。其中T是参数类型,R是返回值类型。
@FunctionalInterface
public interface Convertor<T,R> {
    R change(T t);
}
  //使用Lambda表达式实现Convertor接口,实现取字符串的首字母的功能
        Convertor<String,Character> c1 = (String str)-> {return str.charAt(0);};
        System.out.println(c1.change("hello"));

三、方法引用与构造器引用

Lambda表达式是可以简化函数式接口的变量与形参赋值的语法。而方法引用和构造器引用是为了简化Lambda表达式的。

当Lambda表达式满足一些特殊的情况时,还可以再简化:

(1)Lambda体只有一句语句,并且是通过调用一个对象的/类现有的方法来完成的

(2)并且Lambda表达式的形参正好全部用上,Lambda体中没有额外的数据参与

序号语法格式场景
1实例对象名::实例方法Lambda表达式有多个形参,Lambda体是调用Lambda体外的某个实例对象的实例方法完成,并且Lambda表达式的形参正好依次按顺序作为该方法调用的实参
2类名::静态方法Lambda表达式有多个形参,Lambda体是调用某个类的静态方法完成,并且Lambda表达式的形参正好依次按顺序作为该方法调用的实参
3类名::实例方法Lambda表达式只有1个形参,该参数正好Lambda体中调用方法的对象
Lambda表达式有多个形参,其中第1个参数正好是Lambda体中调用方法的对象,其余形参正好依次按顺序作为该方法调用的实参
4类名::new当Lambda表达式是一个new表达式,并且Lambda表达式形参正好依次按顺序作为所调用构造器的实参
5数组类型名::new当Lambda表达式是一个创建数组对象的new表达式,Lambda表达式的形参正好是创建数组的长度
List<Integer> list = Arrays.asList(1,3,4,8,9);
        //list.forEach(t -> System.out.println(t));
        //用方法引用再简化
        list.forEach(System.out::println);



//        File[] subFiles = dir.listFiles(sub->sub.isFile());
        File[] subFiles = dir.listFiles(File::isFile);

      //这个需求中我们关心的是什么? 如何比较两个字符串的大小,至于对象不重要
//        Arrays.sort(arr, (o1, o2) -> Integer.compare(o1.length(), o2.length()));
//        Arrays.sort(arr, Comparator.comparingInt(s->s.length()));
        Arrays.sort(arr, Comparator.comparingInt(String::length));


  Arrays.sort(arr, (s1,s2) -> s1.compareToIgnoreCase(s2));

        //用方法引用简化
        /*
         * Lambda表达式的形参,第一个(例如:s1),正好是调用方法的对象,剩下的形参(例如:s2)正好是给这个方法的实参
         */
        Arrays.sort(arr, String::compareToIgnoreCase);


 ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"张三","李四","王五");
        //用list集合中的姓名,创建一个一个的Student对象
//        ArrayList<Student>

     /*   List<Student> result = list.stream()
                .map(name -> new Student(name)) //Function<T,R> R apply(T t)
                .collect(Collectors.toList());*/

        List<Student> result = list.stream()
                .map(Student::new) //Function<T,R> R apply(T t)
                .collect(Collectors.toList());

        result.forEach(System.out::println);


 Optional<Integer> opt1 = Optional.ofNullable(16);
//        Optional<String[]> opt2 = opt1.map(len -> new String[len]);
        Optional<String[]> opt2 = opt1.map(String[]::new);
        System.out.println(opt2.orElse(new String[0]).length);

四、Optional类

为了解决空指针异常
Optional实际上是个容器:它可以保存类型T的值,或者仅仅保存null。

序号构造器或方法描述
1static Optional empty()用来创建一个空的Optional
2static Optional of(T value)用来创建一个非空的Optional
3static Optional ofNullable(T value)用来创建一个可能是空,也可能非空的Optional
4T get()返回Optional容器中的对象。要求Optional容器必须非空。T get()与of(T value)使用是安全的
5T orElse(T other)如果Optional容器中非空,就返回所包装值,如果为空,就用orElse(T other)other指定的默认值(备胎)代替。一般orElse(T other) 与ofNullable(T value)配合使用
6T orElseGet(Supplier<? extends T> other)如果Optional容器中非空,就返回所包装值,如果为空,就用Supplier接口的Lambda表达式提供的值代替
7 T orElseThrow(Supplier<? extends X> exceptionSupplier)如果Optional容器中非空,就返回所包装值,如果为空,就抛出你指定的异常类型代替原来的NoSuchElementException
8boolean isPresent()判断Optional容器中的值是否存在
9void ifPresent(Consumer<? super T> consumer)判断Optional容器中的值是否存在,如果存在,就对它进行Consumer指定的操作,如果不存在就不做
10 Optional map(Function<? super T,? extends U> mapper)判断Optional容器中的值是否存在,如果存在,就对它进行Function接口指定的操作,如果不存在就不做
//其他方法类似
String str = null;
        Optional<String> opt = Optional.ofNullable(str);
//		System.out.println(opt.get());//java.util.NoSuchElementException: No value present
        String string = opt.orElse("javaxiaoyu");
         System.out.println(string);   //javaxiaoyu

相关文章:

  • Linux常用命令
  • 植物大战 仿函数——C++
  • 自动写代码?别闹了!
  • Linux中sudo,su与su -命令的区别
  • 因为锁的问题,我们被扣了1万
  • 当我尝试问了chatGPT几个问题之后,我感到了危机......
  • Spring 事务管理详解及使用
  • 第十四届蓝桥杯三月真题刷题训练——第 9 天
  • 【C语言】指针的深度理解(一)
  • python带你成功复刻热门手机游戏——飞翔的小鸟
  • Redis源码---整体架构
  • STM32之SPI
  • 课设-机器学习课设-实现新闻分类
  • 什么是刺猬理念
  • 软件测试用例篇(5)
  • 【CSS】快速入门笔记
  • 设计模式之不变模式
  • 【LeetCode】33. 搜索旋转排序数组、1290. 二进制链表转整数
  • HTTPS协议之SSL/TLS详解(下)
  • 2023金三银四常见Handler面试总结,附带答案
  • https://app.hackthebox.com/machines/Inject
  • Spring —— Spring简单的读取和存储对象 Ⅱ
  • 渗透测试之冰蝎实战
  • Mybatis、TKMybatis对比
  • Microsoft Office 2019(2022年10月批量许可版)图文教程
  • 《谷粒商城基础篇》分布式基础环境搭建
  • 哈希表题目:砖墙
  • Vue 3.0 选项 生命周期钩子
  • 【车载嵌入式开发】AutoSar架构入门介绍篇
  • 【计算机视觉 | 目标检测】DETR风格的目标检测框架解读