• 【Java8新特性】- Lambda表达式


    Java8新特性 - Lambda表达式

    😄生命不息,写作不止
    🔥 继续踏上学习之路,学之分享笔记
    👊 总有一天我也能像各位大佬一样
    🏆 一个有梦有戏的人 @怒放吧德德
    🌝分享学习心得,欢迎指正,大家一起学习成长!

    lambda表达式.jpg

    简介

    Lambda 表达式(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),即没有函数名的函数。Lambda表达式可以表示闭包(注意和数学传统意义上的不同)。这是来自百度百科对lambda的介绍,实际上lambda就是简化了匿名函数的编写,只需一行代码就能代替繁杂的代码。
    lambda主要是可以非常简洁的形式调用我们的匿名函数接口。

    简单例子

    首先声明一个接口,并且编写实现类实现这个接口

    // UserService
    public interface UserService {
        void add();
    }
    // UserServiceImpl
    public class UserServiceImpl implements UserService {
        @Override
        public void add() {
            System.out.println("UserServiceImpl --- add");
        }
    }
    

    一般来说是通过new实现类从而拿到实现中的add方法,如果通过new接口类就需要使用匿名内部类的形式调用接口。IDEA会提示使用lambda表达式。
    image.png

    如下代码可以看一下对比。

    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        userService.add();
    
        UserService user = new UserService() {
            @Override
            public void add() {
                // 匿名内部接口业务逻辑
                System.out.println("匿名内部接口业务逻辑");
            }
        };
    
        // Lamdba表达式
        UserService user2 = () -> System.out.println("匿名内部接口业务逻辑");
        user2.add();
    }
    

    Lambda表达式的规范

    Java中的Lambda表达式的规范,必须是为函数接口。

    使用函数式接口

    使用Lambda表达式依赖于函数式接口:
    1、使用@FunctionalInterface注解来声明函数接口
    2、在这个函数接口中只能有一个抽象方法
    3、在函数接口中可以定义object类中的方法
    4、可以有静态方法
    *函数式接口(Functional Interface) 就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
    更多函数式接口可以查看菜鸟联盟https://www.runoob.com/java/java8-functional-interfaces.html

    函数式接口的定义

    通过@FunctionalInterface注解声明接口类,在java中也有许多内置的函数接口,详情看菜鸟联盟。

    @FunctionalInterface // 声明函数式接口
    public interface UserService {
        void add();
    
        String toString(); // Object类中的方法
    
        static void hello() { // 静态方法
            System.out.println("hello");
        }
    }
    

    Lambda表达式的基础语法

    表达式格式

    (参数) -> {方法体}

    格式说明

    ():存放参数,留空就代表没有参数。函数接口的参数列表,不需要写类型,需要定义参数名称。
    ->:是新引入的语法格式,代表指向动作。
    {}:方法体

    无参方法调用

    在接口类中声明一个无参的抽象方法,通过lambda表达式来完成调用。()不需要存放参数,最后将会返回一个实现类对象。在通过调用对象的get方法。

    // 匿名内部接口业务逻辑
    UserService user2 = () -> System.out.println("匿名内部接口业务逻辑");
    user2.get();
    

    更加简便的写法

    ((UserService)() -> System.out.println("匿名内部接口业务逻辑")).get();
    

    含参数方法调用

    在MathService定义一个含有参数的抽象方法,原本的方式通过匿名内部接口。可以通过使用lambda表达式更加简便,并且可以省去{},也不需要return。

    MathService math = new MathService() {
        @Override
        public Integer add(int a, int b) {
            return a + b;
        }
    };
    
    MathService mathService = (a, b) -> a + b;
    
    System.out.println(mathService.add(1, 1));
    

    方法引入

    规则

    方法引入需要遵循规范:方法参数列表、返回类型与函数接口参数列表与返回类型必须
    要保持一致。
    方法引入的规则:
           静态方法引入: 类名::(静态)方法名称
           对象方法引入: 类名:: 实例方法名称
           实例方法引入:new对象 对象实例::方法引入
           构造函数引入:类名::new
    方法引用提供了非常有用的语法,可以直接引用已有的java类或对象的方法或构造器。方法引用其实也离不开Lambda表达式,
    与lambda联合使用 ,方法引用可以使语言的构造更加紧凑简洁,减少冗余代码。方法引用提供非常有用的语法,可以直接引用已有的java类或者对象中方法或者构造函数,
    方法引用需要配合Lambda表达式语法一起使用减少代码的冗余性问题。
    1664976332232.png

    实例

    构造器引用

    通过构造器引用实例化对象,

    public static void main(String[] args) {
        // 使用匿名内部类
        CarService carService = new CarService() {
            @Override
            public Car getCar() {
                return null;
            }
        };
        // 使用引用方法
        CarService car = Car::new;
        car.getCar();
    }
    public class Car {
        private String name;
        private String brand;
    
        public Car() {
        }
    }
    public interface CarService {
        Car getCar();
    }
    

    静态方法引入

    UserService u = UserService::hello;
    u.get();
    

    在userservice中,有一个抽象get方法和一个静态方法

    @FunctionalInterface
    public interface UserService {
        void get();
        static void hello() {
            System.out.println("hello");
        }
    }
    

    运行后会输出hello

    hello
    
    Process finished with exit code 0
    

    对象方法引入

    这是一种更为方便的写法, 我记得在mybatis-plus使用自带条件查询的时候会用到这种方式,以下用简单例子来实现。java8中提供了public interface Function的类,这个类也是使用@FunctionalInterface注解,可见也是函数式接口。
    代码:

    // 在car中声明一个方法
    public class Car {
        public String Info() {
            return "保时捷 - 帕拉梅拉";
        }
    }
    // 函数式接口
    @FunctionalInterface
    public interface CarInFoService {
        String getCar(Car car);
    }
    

    测试采用了三种方式进行比较

    public class LambdaTest4 {
        public static void main(String[] args) {
            System.out.println("**************匿名内部类**************");
            CarInFoService carInFoService = new CarInFoService() {
                @Override
                public String getCar(Car car) {
                    return car.Info();
                }
            };
            System.out.println(carInFoService.getCar(new Car()));
            System.out.println("**************lambda**************");
            CarInFoService carInFoService2 = (car) -> car.Info();
            System.out.println(carInFoService2.getCar(new Car()));
            System.out.println("**************对象方法引入**************");
            CarInFoService carInFoService3 = Car::Info;
            System.out.println(carInFoService3.getCar(new Car()));
    
            // R apply(T t); T  apply方法传递的参数类型 : R apply 方法返回的类型
            Function function = String::length;
            System.out.println(function.apply("lyd_code"));
        }
    }
    

    结果
    image.png

    Lambda表达式遍历

    通过foreach循环遍历,forEach实际上需要使用匿名内部类Consumer
    image.png
    代码如下

    public class LambdaTest5 {
        public static void main(String[] args) {
            ArrayList list = new ArrayList<>();
            list.add("lyd");
            list.add("tom");
            list.add("jack");
            list.forEach(new Consumer() {
                @Override
                public void accept(String s) {
                    System.out.println("name: " + s);
                }
            });
            System.out.println("lambda表达式");
            /**
             * s:遍历链表所得到的元素字符串
             */
            list.forEach(s -> System.out.println("name: " + s));
        }
    }
    

    测试结果
    image.png

    Lambda排序

    通过Comparator匿名内部类

    public class LambdaTest6 {
        public static void main(String[] args) {
            ArrayList students = new ArrayList<>();
            students.add(new Student("lyd", 99));
            students.add(new Student("lkj", 55));
            students.add(new Student("llm", 67));
            students.add(new Student("lss", 87));
    
    //        students.sort(new Comparator() {
    //            @Override
    //            public int compare(Student o1, Student o2) {
    //                return o1.getScore() - o2.getScore();
    //            }
    //        });
    
            students.sort((o1, o2) -> o1.getScore() - o2.getScore());
    
            students.forEach(student -> System.out.println("name: " + student.getName() + " score: " + student.getScore()));
        }
    }
    

    结果
    image.png

    为了写好博客,包括代码、文字,我是经过斟酌了的,如果有错误,欢迎指正!感谢观看!
    👍创作不易,如有错误请指正,感谢观看!记得点赞哦!👍

  • 相关阅读:
    Mybatis sql 控制台格式化
    毛玻璃员工卡片悬停效果
    CocosCreator接入wwise的CocosAndroid.mk
    2.14日学习打卡----初学Zookeeper(一)
    Vue 使用SignalR.JS与Microsoft.AspNetCore.SignalR实时通讯
    AI程序员来了,大批码农要失业
    CSS常用的基础知识点
    『忘了再学』Shell基础 — 19、使用declare命令声明变量类型
    znai: 使用Markdown编写Java文档系统
    神经元细胞属于什么细胞,人体有多少神经元细胞
  • 原文地址:https://www.cnblogs.com/lyd-code/p/16756479.html