Java-Lambda表达式详解

引言:为什么需要 Lambda 表达式?

在 Java 8 之前,处理需要传递代码块的场景(如事件监听、线程任务)通常依赖匿名内部类。这种方式代码冗余,可读性差。例如:

// 传统匿名内部类实现 Runnable
new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello from anonymous class!");
    }
}).start();

Java 8 引入 Lambda 表达式,允许以更简洁的方式表示函数式接口的实例,同时推动函数式编程范式的发展。

一、Lambda表达式的引入

Lambda 的核心语法为:

(参数列表) -> { 方法体 }

可简化为以下形式:

单参数无类型推断:a -> a + 1

无参数:() -> System.out.println("Hi")

多参数:(x, y) -> x > y

多行代码:

(name) -> {
    String greeting = "Hello, " + name;
    System.out.println(greeting);
}

示例对比:用 Lambda 重写 Runnable

new Thread(() -> System.out.println("Hello from Lambda!")).start();

代码行数减少 60%,逻辑更聚焦。

import java.util.Comparator;
import java.util.TreeSet;

/**
 * ClassName: LambdaTest01
 * Description: 先体会一下Java8的新特性:Lambda表达式
 */
public class LambdaTest01 {
    public static void main(String[] args) {
        // TreeSet集合中的元素是可以自动排序的。
        // TreeSet集合是怎么排序的? 两种方式
        // 第一种方式:如果比较规则固定不变,可以让TreeSet集合中的元素实现java.lang.Comparable接口。
        // 第二种方式:创建TreeSet集合的时候,给TreeSet集合传递一个比较器对象,比较器实现java.util.Comparator接口。
        // 以下是匿名内部类的方式
        /*TreeSet<User> users = new TreeSet<>(new Comparator<User>() {
            @Override
            public int compare(User o1, User o2) {
                return o1.getAge() - o2.getAge();
            }
        });*/

        //TreeSet<User> users1 = new TreeSet<>((User o1, User o2) -> { return o2.getAge() - o1.getAge(); });

        TreeSet<User> users1 = new TreeSet<>((o1, o2) ->  o2.getAge() - o1.getAge() );

        User user1 = new User(20);
        User user2 = new User(30);
        User user3 = new User(40);
        User user4 = new User(10);

        users1.add(user1);
        users1.add(user2);
        users1.add(user3);
        users1.add(user4);

        System.out.println(users1);
    }
}

class TT {
    public void doSome(){}
}

interface  A{

}
class B implements A{

}

class User {
    private int age;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public User(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "age=" + age +
                '}';
    }
}

运行结果:

二、函数式编程思想的概述

Java从诞生之日起就一直倡导“一切皆对象”,在Java语言中面向对象(OOP)编程就是一切,但是随着Python和Scala等语言的崛起和新技术的挑战,Java也不得不做出调整以便支持更加广泛的技术要求,即Java语言不但支持OOP还支持OOF(面向函数编程)。 JDK1.8引入Lambda表达式之后,Java语言也开始支持函数式编程,但是Lambda表达式不是Java语言最早使用的,目前C++、C#、Python、Scala等语言都支持Lambda表示。

  • 面向对象的思想

    • 做一件事情,找一个能解决这个事情的对象,然后调用对象的方法,最终完成事情。

  • 函数式编程思想

    • 只要能获得结果,谁去做的,怎么做的都不重要,重视的是结果,不重视实现过程。

在函数式编程语言中,函数被当成一等公民对待。在将函数当成一等公民的编程语言中,Lambda表达式的类型是函数,但是Lambda表达式却是一个对象,而不是函数,它们必须依附于一类特别的对象类型,也就是所谓的函数式接口。 简单点说,JDK1.8中的Lambda表达式就是一个函数式接口的实例,这就是Lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口的实例,那么该对象就可以使用Lambda表达式来表示

1.如何去理解函数式接口

能够使用Lambda表达式的一个重要依据是必须有相应的函数式接口,所谓的函数式接口,指的就是“一个接口中有且只能有一个抽象方法”。也就是说,如果一个接口只有一个抽象方法,那么该接口就是一个函数式接口。 如果我们在接口上声明了 @FunctionalInterface 注解,那么编译器就会按照函数式接口的定义来要求该接口,也就是该接口中有且只能定义一个抽象方法,如果该接口中定义了多个或0个抽象方法,则程序编译时就会报错。 【示例】定义一个函数式接口

@FunctionalInterface
public interface Flyable {
    // 在函数式接口中,我们有且只能定义一个抽象方法
    void showFly();
    // 但是,可以定义任意多个默认方法或静态方法
    default void show() {
        System.out.println("JDK1.8之后,接口还可以定义默认方法和静态方法");
    }
}

另外,从某种意义上来说,只要你保证你的接口中有且只有一个抽象方法,则接口中没有使用 @FunctionalInterface 注解来标注,那么该接口也依旧属于函数式接口。

在以下代码中,Flyable接口中没有使用@FunctionalInterface 注解,但是Flyable接口中只存在一个抽象方法,因此Flyable接口依旧属于函数式接口,那么使用Lambda表达式就可以表示Flyable 接口的实例,代码如下:

/**
 * 没有使用@FunctionalInterface标注的接口
 */
public interface Flyable {
    void showFly();
}
/**
 * 测试类
 */
public class Test01 {
    public static void main(String[] args) {
        // 使用lambda表示来表示Flyable接口的实例
        Flyable flyable = () -> {
            System.out.println("小鸟自由自在的飞翔");
        };
        // 调用Flyable接口的实例的showFly()方法
        flyable.showFly();
    }
}

三、Lambda和匿名内部类

  • 所需类型不同

    • 匿名内部类:可以是接口,抽象类,具体类。

    • Lambda表达式:只能是接口。

  • 使用限制不同

    • 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类。

    • 如果接口中有多个抽象方法,则就只能使用匿名内部类,而不能使用Lambda表达式。

  • 实现原理不同

    • 匿名内部类:编译之后,会生成一个单独的.class字节码文件。

    • Lambda表达式:编译之后,没有生成一个单独的.class字节码文件。

/**
 * ClassName: LambdaTest02
 * Description:
 *      Lambda表达式和匿名内部类的区别:
 *          所需类型不同:
 *              匿名内部类,可以是抽象类,也可以是接口。
 *              Lambda表达式,只能是接口。
 *          使用限制不同:
 *              Lambda表达式使用的接口中要求有且只有一个抽象方法。
 *              匿名内部类方式使用的接口可以有多个抽象方法。
 *          实现原理不同:
 *              采用匿名内部类的话,编译之后会生成一个.class文件。
 *              采用Lambda表达式的话,编译之后不会生成单独的.class文件。
 */
public class LambdaTest02 {

    public static void main(String[] args) {

        // 匿名内部类方式(匿名内部类可以是一个抽象类)
        LambdaTest02.test(new Animal() {
            @Override
            public void run() {
                System.out.println("Animal run....");
            }
        });

        // 尝试将上面的代码修改为Lambda表达式方式
        // 编译报错,原因是:只有接口才可以使用Lambda表达式
        //LambdaTest02.test(() -> { System.out.println("Animal run...."); });

        // 匿名内部类
        /*LambdaTest02.doFly(new Flyable() {
            @Override
            public void run() {
                System.out.println("run.....");
            }

            @Override
            public void fly() {
                System.out.println("fly.....");
            }
        });*/

        // 尝试使用Lambda表达式
        // Lambda表达式使用的接口必须是函数式接口。(必须是接口,而且接口中有且只有一个抽象方法。)
        //LambdaTest02.doFly(() -> { System.out.println("run....."); });


    }

    public static void test(Animal a){
        a.run();
    }

    public static void doFly(Flyable f){
        f.fly();
        f.run();
    }
}

abstract class Animal{
    public abstract void run();
}

interface Flyable {
    void run();
    void fly();
}

运行结果:

四、Lambda表达式的使用

1.Lambda表达式的语法

Lambda表达式本质就是一个匿名函数,在函数的语法中包含返回值类型、方法名、形参列表和方法体等,而在Lambda表达式中我们只需要关心形参列表和方法体即可。

在Java语言中,Lambda表达式的语法为“(形参列表) -> {方法体}”,其中“->”为 lambda操作符或箭头操作符,

“形参列表”为对应接口实现类中重写方法的形参列表,

“方法体”为对应接口实现类中重写方法的方法体。

接下来,我们就以匿名内部类为例,从而将匿名内部类演化为Lambda表达式,代码如下:

List<Integer> list = Arrays.asList(3, 6, 1, 7, 2, 5, 4);
Collections.sort(list, new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o2 - o1;
    }
});
System.out.println("排序后:" + list);

在以上的匿名内部类中,黄色背景颜色标注的代码都属于不可变的固定代码,而红色背景颜色标注的代码,属于可变的并且是完成该功能的核心代码。因此,将此处的匿名内部类转化为Lambda表达式,我们只需保留红色部分的形参列表和方法体即可,对应的Lambda表达式代码实现如下:

List<Integer> list = Arrays.asList(3, 6, 1, 7, 2, 5, 4);
Collections.sort(list, (Integer o1, Integer o2) -> {
    return o2 - o1;
});
System.out.println("排序后:" + list);
*          Lambda表达式的语法格式:
*              (形式参数列表) -> {
*                  方法体;
*              }
/**
 * ClassName: LambdaTest03
 * Description:
 *          Lambda表达式的语法格式:
 *              (形式参数列表) -> {
 *                  方法体;
 *              }
 */
public class LambdaTest03 {
    public static void main(String[] args) {

        List<Integer> list = Arrays.asList(100, 200, 300, 250);

        // 对List集合中的元素排序
        //Collections.sort(list);

        // 为了讲解这个Lambda表达式的基础语法,刻意绕弯了。
        // 这是匿名内部类的方式
        /*Collections.sort(list, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });*/

        // 改成Lambda表达式的形式
        //Collections.sort(list, (Integer a, Integer b) -> { return b - a; });

        Comparator<Integer> comparator = (Integer a, Integer b) -> { return a - b; };
        Collections.sort(list, comparator);

        // 输出排序后的
        System.out.println(list);
    }
}

运行结果:

2.Lambda表达式的基本使用

接下来,我们以自定义的函数式接口为例,先从匿名对象的实现过程,慢慢演变为Lambda表达式的实现过程。另外,使用Lambda表达式的时候,则必须有上下文环境,才能推导出Lambda对应的接口类型。

⑴.无返回值函数式接口

情况一:无返回值无参数

/**
 * ClassName: LambdaTest04
 * Description:
 *          Lambda表达式的使用:关于无返回值无参数的函数式接口。
 */
public class LambdaTest04 {
    public static void main(String[] args) {
        // 匿名内部类方式
        NoParameterNoReturn npnr = new NoParameterNoReturn() {
            @Override
            public void test() {
                System.out.println("无返回值无参数的test方法执行了。");
            }
        };
        npnr.test();

        // 改成Lambda表达式
        NoParameterNoReturn npnr2 = () -> { System.out.println("无返回值无参数的test方法执行了。"); };
        npnr2.test();

        // 精简
        NoParameterNoReturn npnr3 = () -> System.out.println("无返回值无参数的test方法执行了。");
        npnr3.test();
    }
}
//无返回值,无参数
@FunctionalInterface
interface NoParameterNoReturn {
    void test();
}

运行结果:

情况二:无返回值一个参数

/**
 * ClassName: LambdaTest05
 * Description:
 *          Lambda表达式的使用:关于无返回值一个参数的函数式接口。
 */
public class LambdaTest05 {
    public static void main(String[] args) {
        // 匿名内部类的方式
        OneParameterNoReturn opnr = new OneParameterNoReturn() {
            @Override
            public void test(Integer value) {
                System.out.println("Integer-->" + value);
            }
        };
        opnr.test(1000);

        // Lambda表达式方式
        OneParameterNoReturn opnr2 = (Integer value) -> { System.out.println("Integer-->" + value); };
        opnr2.test(2000);

        // 精简
        OneParameterNoReturn opnr3 = value -> System.out.println("Integer-->" + value);
        opnr3.test(2000);
    }
}

@FunctionalInterface
interface OneParameterNoReturn {
    void test(Integer value);
}

运行结果:

情况三:无返回值多个参数

/**
 * ClassName: LambdaTest06
 * Description:
 *          Lambda表达式的使用:关于无返回值多个参数的函数式接口。

 */
public class LambdaTest06 {
    public static void main(String[] args) {
        // 匿名内部类的方式
        MoreParameterNoReturn mpnr = new MoreParameterNoReturn() {
            @Override
            public void test(Integer value1, Integer value2) {
                System.out.println(value1 + value2);
            }
        };
        mpnr.test(100, 200);

        // Lambda表达式方式
        MoreParameterNoReturn mpnr2 = (Integer value1, Integer value2) -> { System.out.println(value1 + value2); };
        mpnr2.test(300, 400);

        // 精简
        MoreParameterNoReturn mpnr3 = (value1, value2) -> System.out.println(value1 + value2);
        mpnr3.test(300, 400);
    }
}

@FunctionalInterface
interface MoreParameterNoReturn {
    void test(Integer value1, Integer value2);
}

运行结果:

⑵.有返回值函数接口

情况一:有返回值无参数

/**
 * ClassName: LambdaTest07
 * Description:
 *          Lambda表达式的使用:关于有返回值无参数的函数式接口。
 */
public class LambdaTest07 {
    public static void main(String[] args) {
        // 匿名内部类的方式
        NoParameterHasReturn nphr = new NoParameterHasReturn() {
            @Override
            public Integer test() {
                return 300;
            }
        };
        System.out.println(nphr.test());

        // Lambda表达式的方式
        NoParameterHasReturn nphr2 = () -> { return 500; };
        System.out.println(nphr2.test());

        // 精简
        NoParameterHasReturn nphr3 = () -> 500;
        System.out.println(nphr3.test());
    }
}

@FunctionalInterface
interface NoParameterHasReturn {
    Integer test();
}

运行结果:

情况二:有返回值一个参数

/**
 * ClassName: LambdaTest08
 * Description:
 *          Lambda表达式的使用:关于有返回值一个参数的函数式接口。
 */
public class LambdaTest08 {
    public static void main(String[] args) {
        // 匿名内部类的方式
        OneParameterHasReturn ophr = new OneParameterHasReturn() {
            @Override
            public Integer test(Integer value) {
                return value * 2;
            }
        };
        System.out.println(ophr.test(100));

        // Lambda表达式的方式
        OneParameterHasReturn ophr2 = (Integer value) -> { return value * 2; };
        System.out.println(ophr2.test(200));

        // 精简
        OneParameterHasReturn ophr3 = value -> value * 2;
        System.out.println(ophr3.test(200));
    }
}

@FunctionalInterface
interface OneParameterHasReturn {
    Integer test(Integer value);
}

运行结果:

情况三:有返回值多个参数

/**
 * ClassName: LambdaTest09
 * Description:
 *          Lambda表达式的使用:关于有返回值多个参数的函数式接口。

 */
public class LambdaTest09 {
    public static void main(String[] args) {
        // 匿名内部类方式
        MoreParameterHasReturn mphr = new MoreParameterHasReturn() {
            @Override
            public Integer test(Integer value1, Integer value2) {
                return value1 + value2;
            }
        };
        System.out.println(mphr.test(1, 2));

        // Lambda表达式的方式
        MoreParameterHasReturn mphr2 = (Integer value1, Integer value2) -> { return value1 + value2; };
        System.out.println(mphr2.test(3, 4));

        // 精简
        MoreParameterHasReturn mphr3 = (a, b) -> a + b;
        System.out.println(mphr3.test(3, 4));

    }
}

@FunctionalInterface
interface MoreParameterHasReturn {
    Integer test(Integer value1, Integer value2);
}

运行结果:

3.Lambda表达式的语法精简

在以上代码中,虽然Lambda表达式的语法已经很简洁了,但是Lambda表达式的语法格式还可以更加的精简,从而写出更加优雅的代码,但是相应的代码可读性也会变差。 在以下的应用场景中,我们就可以对Lambda表达式的语法进行精简,场景如下:

  1. 形参类型可以省略,如果需要省略,则每个形参的类型都要省略。

  2. 如果形参列表中只存在一个形参,那么形参类型和小括号都可以省略。

  3. 如果方法体当中只有一行语句,那么方法体的大括号也可以省略。

  4. 如果方法体中只有一条return语句,那么大括号可以省略,且必须去掉return关键字。

接下来,我们就对以下的Lambda表达式代码进行精简,从而写出更加优雅的代码。

4.四个基本的函数式接口

名字接口名对应的抽象方法
消费Consumer<T>void accept(T t);
生产Supplier<T>T get();
转换Function<T, R>R apply(T t);
判断Predicate<T>boolean test(T t);

以上的函数式接口都在java.util.function包中,通常函数接口出现的地方都可以使用Lambda表达式,所以不必记忆函数接口的名字,这些函数式接口及子接口在后续学习中很常用。

五、Lambda表达式的方法引用

1.方法引用的概述

我们在使用Lambda表达式的时候,如果Lambda表达式的方法体中除了调用现有方法之外什么都不做,满足这样的条件就有机会使用方法引用来实现。

在以下的代码中,在重写的apply()方法中仅仅只调用了现有Math类round()方法,也就意味着Lambda表达式中仅仅只调用了现有Math类round()方法,那么该Lambda表达式就可以升级为方法引用,案例如下:

// 需求:实现小数取整的操作
// 方式一:使用匿名对象来实现
Function<Double, Long> function1 = new Function<Double, Long>() {
    @Override
    public Long apply(Double aDouble) {
        return Math.round(aDouble);
    }
};
System.out.println(function1.apply(3.14));

// 方式二:使用Lambda表达式来实现
Function<Double, Long> function2 = aDouble -> Math.round(aDouble);
System.out.println(function2.apply(3.14));

// 方式三:使用方法引用来实现
Function<Double, Long> function3 = Math :: round;
System.out.println(function3.apply(3.14));

对于方法引用,我们可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。

在Lambda表达式的方法引用中,主要有实例方法引用、静态方法引用、特殊方法引用和构造方法引用、数组引用这五种情况,接下来我们就对这五种情况进行讲解。

2.实例方法引用

语法:对象 :: 实例方法

特点:在Lambda表达式的方法体中,通过“对象”来调用指定的某个“实例方法”。 要求:函数式接口中抽象方法的返回值类型和形参列表 与 内部通过对象调用某个实例方法的返回值类型和形参列表 保持一致。

【示例】实例化Consumer接口的实现类对象,并在重写的accept()方法中输出形参的值

// 方式一:使用匿名内部类来实现
Consumer<String> consumer1 = new Consumer<String>() {
    @Override
    public void accept(String str) {
        System.out.println(str);
    }
};
consumer1.accept("hello world");

// 方式二:使用Lambda表达式来实现
Consumer<String> consumer2 = str -> System.out.println(str);
consumer2.accept("hello world");

// 方式三:使用方法引用来实现
Consumer<String> consumer3 = System.out :: println;
consumer3.accept("hello world");

【示例】实例化Supplier接口的实现类对象,并在重写方法中返回Teacher对象的姓名

Teacher teacher = new Teacher("ande", 18);
// 方式一:使用匿名内部类来实现
Supplier<String> supplier1 = new Supplier<String>() {
    @Override
    public String get() {
        return teacher.getName();
    }
};
System.out.println(supplier1.get());

// 方式二:使用Lambda表达式来实现
Supplier<String> supplier2 = () -> teacher.getName();
System.out.println(supplier2.get());

// 方式三:使用方法引用来实现
Supplier<String> supplier3 = teacher :: getName;
System.out.println(supplier3.get());

运行实例1:

import java.util.function.Supplier;

/**
 * ClassName: LambdaTest10
 * Description: 实例方法引用
 *          语法格式:
 *              对象::实例方法名
 *
 *          满足什么条件的时候可以使用实例方法引用?
 *              函数式接口中的   返回值类型   和    形参
 *              与
 *              内部调用的方法的 返回值类型   和    形参
 *              一致。

 */
public class LambdaTest10 {
    public static void main(String[] args) {
        // 使用生产型接口:Supplier
        // 匿名内部类的方式
        Teacher teacher = new Teacher("老杜");
        Supplier<String> supplier = new Supplier<String>() {
            @Override
            public String get() {
                return teacher.getName();
            }
        };
        System.out.println(supplier.get());

        // 以上是否符合“实例方法引用”的条件?
        // 先修改为Lambda表达式
        Supplier<String> supplier1 = () -> teacher.getName();
        System.out.println(supplier1.get());

        // 使用“实例方法引用”精简
        Supplier<String> supplier2 = teacher::getName;
        System.out.println(supplier2.get());

    }
}

class Teacher {
    private String name;

    public Teacher(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "name='" + name + '\'' +
                '}';
    }
}

运行结果:

运行代码2:

import java.util.function.Consumer;

/**
 * ClassName: LambdaTest11
 * Description: 实例方法引用
 */
public class LambdaTest11 {
    public static void main(String[] args) {
        // 匿名内部类的方式
        // 使用消费型的函数式接口
        Consumer<String> consumer = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };
        consumer.accept("动力节点");

        // 修改为Lambda表达式
        Consumer<String> consumer1 = s -> System.out.println(s);
        consumer1.accept("动力节点");

        // 使用 实例方法引用 精简
        Consumer<String> consumer2 = System.out::println;
        consumer2.accept("动力节点");
    }
}

运行结果:

3.静态方法引用

语法:类 :: 静态方法

特点:在Lambda表达式的方法体中,通过“类名”来调用指定的某个“静态方法”。 要求:函数式接口中抽象方法的返回值类型和形参列表 与 内部通过类名调用某个静态方法的返回值类型和形参列表保持一致。

【示例】实例化Function接口的实现类对象,并在重写的方法中返回小数取整的结果

// 方式一:使用匿名内部类来实现
Function<Double, Long> function1 = new Function<Double, Long>() {
    @Override
    public Long apply(Double aDouble) {
        return Math.round(aDouble);
    }
};
System.out.println(function1.apply(3.14));

// 方式二:使用Lambda表达式来实现
Function<Double, Long> function2 = aDouble -> Math.round(aDouble);
System.out.println(function2.apply(3.14));

// 方式三:使用方法引用来实现
Function<Double, Long> function3 = Math :: round;
System.out.println(function3.apply(3.14));
import java.util.function.Function;

/**
 * ClassName: LambdaTest12
 * Description: 静态方法引用
 *
 *          语法格式:
 *              类名::静态方法名
 *
 *          条件:
 *              函数式接口中的方法的     返回值类型     和    形参
 *              与
 *              内部调用静态方法的      返回值类型      和    形参
 *              一致。

 */
public class LambdaTest12 {
    public static void main(String[] args) {
        // 匿名内部类方式
        // 使用转换型函数式接口
        Function<Double, Long> function = new Function<Double, Long>() {
            @Override
            public Long apply(Double value) {
                return Math.round(value);
            }
        };
        System.out.println(function.apply(3.14));

        // Lambda表达式
        Function<Double, Long> function2 = value -> Math.round(value);
        System.out.println(function2.apply(5.67));

        // 静态方法引用改进
        Function<Double, Long> function3 = Math::round;
        System.out.println(function3.apply(5.67));
    }
}

运行结果:

4.特殊方法引用

语法:类名 :: 实例方法

特点:在Lambda表达式的方法体中,通过方法的第一个形参来调用指定的某个“实例方法”。

要求:把函数式接口中抽象方法的第一个形参作为方法的调用者对象,并且从第二个形参开始(或无参)可以对应到被调用实例方法的参数列表中,并且返回值类型保持一致。 【示例】使用Comparator比较器,来判断两个小数的大小

// 方式一:使用匿名内部类来实现
Comparator<Double> comparator1 = new Comparator<Double>() {
    @Override
    public int compare(Double o1, Double o2) {
        return o1.compareTo(o2);
    }
};
System.out.println(comparator1.compare(10.0, 20.0));

// 方式二:使用Lambda表达式来实现
Comparator<Double> comparator2 = (o1, o2) -> o1.compareTo(o2);
System.out.println(comparator2.compare(10.0, 20.0));

// 方式三:使用方法引用来实现
Comparator<Double> comparator3 = Double :: compareTo;
System.out.println(comparator3.compare(10.0, 20.0));

需求:实例化Function接口的实现类对象,然后获得传入Teacher对象的姓名。

// 方式一:使用匿名内部类来实现
Teacher teacher = new Teacher("ande", 18);
Function<Teacher, String> function1 = new Function<Teacher, String>() {
    @Override
    public String apply(Teacher teacher) {
        return teacher.getName();
    }
};
System.out.println(function1.apply(teacher));

// 方式二:使用Lambda表达式来实现
Function<Teacher, String> function2 = e -> e.getName();
System.out.println(function2.apply(teacher));

// 方式三:使用方法引用来实现
Function<Teacher, String > function3 = Teacher :: getName;
System.out.println(function3.apply(teacher));
import java.util.Comparator;

/**
 * ClassName: LambdaTest13
 * Description: 特殊方法引用
 *          语法格式:
 *              类名::实例方法
 *
 *          条件:
 *              1. 函数式接口中抽象方法的第一个参数作为内部方法调用对象。
 *              2. 从函数式接口的抽象方法的第二参数开始 与 内部调用方法时的参数类型  一致。
 *              3. 函数式接口中的抽象方法返回值类型  与  内部方法返回值类型  一致。

 */
public class LambdaTest13 {
    public static void main(String[] args) {
        // 匿名内部类
        Comparator<Double> comparator = new Comparator<Double>() {
            @Override
            public int compare(Double o1, Double o2) {
                return o1.compareTo(o2);
            }
        };
        System.out.println(comparator.compare(3.14, 5.6));

        // Lambda表达式
        Comparator<Double> comparator2 = (o1, o2) -> o1.compareTo(o2);
        System.out.println(comparator2.compare(3.14, 5.6));

        // 特殊方法引用
        Comparator<Double> comparator3 = Double::compareTo;
        System.out.println(comparator3.compare(3.14, 5.6));

    }
}

运行结果:

import java.util.function.Function;

/**
 * ClassName: LambdaTest14
 * Description: 特殊方法引用
 *          语法格式:
 *              类名::实例方法名
 */
public class LambdaTest14 {
    public static void main(String[] args) {
        // 转换型的函数式接口
        // 匿名内部类
        Function<Vip, String> function = new Function<Vip, String>() {
            @Override
            public String apply(Vip vip) {
                return vip.getName();
            }
        };

        Vip vip = new Vip("老杜");
        System.out.println(function.apply(vip));

        // Lambda表达式
        //Function<Vip, String> function2 = (Vip v) -> {return v.getName();};
        Function<Vip, String> function2 = v -> v.getName();
        System.out.println(function2.apply(vip));

        // 使用“特殊方法引用”来进行精简
        Function<Vip, String> function3 = Vip::getName;
        System.out.println(function3.apply(vip));
    }
}

class Vip {
    private String name;

    @Override
    public String toString() {
        return "Vip{" +
                "name='" + name + '\'' +
                '}';
    }

    public Vip(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

运行结果:

5.构造方法引用

语法:类名 :: new

特点:在Lambda表达式的方法体中,返回指定“类名”来创建出来的对象。

要求:创建对象所调用构造方法形参列表 和 函数式接口中的方法的形参列表 保持一致,并且方法的返回值类型和创建对象的类型保持一致。

【示例】实例化Supplier接口的实现类对象,然后调用重写方法返回Teacher对象

// 方式一:使用匿名内部类来实现
Supplier<Teacher> supplier1 = new Supplier<Teacher>() {
    @Override
    public Teacher get() {
        return new Teacher();
    }
};
System.out.println(supplier1.get());

// 方式二:使用Lambda表达式来实现
Supplier<Teacher> supplier2 = () -> new Teacher();
System.out.println(supplier2.get());

// 方式二:使用构造方法引用来实现
// 注意:根据重写方法的形参列表,那么此处调用了Teacher类的无参构造方法
Supplier<Teacher> supplier3 = Teacher :: new;
System.out.println(supplier3.get());

【示例】实例化Function接口的实现类对象,然后调用重写方法返回Teacher对象

// 方式一:使用匿名内部类来实现
Function<String, Teacher> function1 = new Function<String, Teacher>() {
    @Override
    public Teacher apply(String name) {
        return new Teacher(name);
    }
};
System.out.println(function1.apply("ande"));

// 方式二:使用Lambda表达式来实现
Function<String, Teacher> function2 = name -> new Teacher(name);
System.out.println(function2.apply("ande"));

// 方式二:使用构造方法引用来实现
// 注意:根据重写方法的形参列表,那么此处调用了Teacher类name参数的构造方法
Function<String, Teacher> function3 = Teacher :: new;
System.out.println(function3.apply("ande"));
/**
 * ClassName: LambdaTest15
 * Description: 构造方法引用
 *      语法格式:
 *          类名::new
 *      条件:
 *          函数式接口中的方法的形式参数列表
 *          与
 *          构造方法上的形式参数列表
 *          一致。
 *          并且返回值类型相同。

 */
public class LambdaTest15 {
    public static void main(String[] args) {
        // 匿名内部类方式
        // 使用生产型的函数式接口
        Supplier<Bird> supplier = new Supplier<Bird>() {
            @Override
            public Bird get() {
                return new Bird();
            }
        };
        System.out.println(supplier.get());

        // Lambda表达式
        Supplier<Bird> supplier1 = () -> new Bird();
        System.out.println(supplier1.get());

        // 使用    构造方法引用    精简
        Supplier<Bird> supplier2 = Bird::new;
        System.out.println(supplier2.get());
    }
}

class Bird {

}

运行结果:

/**
 * ClassName: LambdaTest16
 * Description: 构造方法引用
 */
public class LambdaTest16 {
    public static void main(String[] args) {
        // 匿名内部类的方式
        // 使用转换型的函数式接口
        Function<String, Person> function = new Function<String, Person>() {
            @Override
            public Person apply(String name) {
                return new Person(name);
            }
        };
        System.out.println(function.apply("张三"));

        // Lambda表达式的形式
        Function<String, Person> function1 = name -> new Person(name);
        System.out.println(function1.apply("李四"));

        // 使用  构造方法引用  精简
        Function<String, Person> function2 = Person::new;
        System.out.println(function2.apply("李四"));
    }
}

class Person {
    private String name;

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Person(String name) {
        this.name = name;
    }
}

运行结果:

6.数组引用

语法:数组类型 :: new
特点:在Lambda表达式的方法体中,创建并返回指定类型的“数组”。
要求:重写的方法有且只有一个整数型的参数,并且该参数就是用于设置数组的空间长度,并且重写方法的返回值类型和创建数组的类型保持一致。
【示例】实例化Function接口的实现类对象,并在重写方法中返回指定长度的int类型数组

// 方式一:使用匿名内部类来实
Function<Integer, int[]> function1 = new Function<Integer, int[]>() {
    @Override
    public int[] apply(Integer integer) {
        return new int[integer];
    }
};
System.out.println(Arrays.toString(function1.apply(10)));

// 方式二:使用Lambda表达式来实现
Function<Integer, int[]> function2 = num -> new int[num];
System.out.println(Arrays.toString(function2.apply(20)));

// 方式三:使用方法引用来实现
Function<Integer, int[]> function3 = int[] :: new;
System.out.println(Arrays.toString(function3.apply(30)));

import java.util.Arrays;
import java.util.function.Function;

/**
 * ClassName: LambdaTest17
 * Description: 数组引用
 *          语法格式:
 *              数组::new
 *          条件:
 *              1. 函数式接口中的方法只有一个整数型参数。
 *              2. 这个整数型参数正好是数组的长度。
 *              3. 返回值类型相同。
 */
public class LambdaTest17 {
    public static void main(String[] args) {
        // 匿名内部类的方式
        // 转换型函数式接口
        Function<Integer, int[]> function = new Function<Integer, int[]>() {
            @Override
            public int[] apply(Integer integer) {
                return new int[integer];
            }
        };
        int[] nums = function.apply(10);
        System.out.println(Arrays.toString(nums));

        // Lambda表达式
        Function<Integer, int[]> function1 = length -> new int[length];
        nums = function1.apply(20);
        System.out.println(Arrays.toString(nums));

        // 构造方法引用  精简
        Function<Integer, int[]> function2 = int[]::new;
        nums = function2.apply(30);
        System.out.println(Arrays.toString(nums));

    }
}

运行结果:

六、Lambda在集合当中的使用

为了能够让Lambda和Java的集合类集更好的一起使用,集合当中也新增了部分方法,以便与Lambda表达式对接,要用Lambda操作集合就一定要看懂源码。

1.forEach()方法

在Collection集合和Map集合中,都提供了forEach()方法用于遍历集合。 在Collection集合中,提供的forEach()方法的形参为Consumer接口(消费型接口),通过该方法再配合Lambda表达式就可以遍历List和Set集合中的元素。

【示例】遍历List集合中的元素

List<Integer> list = Arrays.asList(11, 22, 33, 44, 55);
// 方式一:使用匿名内部类来实现
list.forEach(new Consumer<Integer>() {
    /**
     * 获得遍历出来的元素
     * @param element 遍历出来的元素
     */
    @Override
    public void accept(Integer element) {
        System.out.println(element);
    }
});

// 方式二:使用Lambda表达式来实现
list.forEach(element -> System.out.println(element));

// 方式三:使用方法引用来实现
list.forEach(System.out :: println);
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

/**
 * ClassName: LambdaTest18
 * Description:
 *          使用集合提供的forEach方法,结合Lambda表达式。
 *          遍历List集合。
 */
public class LambdaTest18 {
    public static void main(String[] args) {
        // List集合
        List<Integer> list = Arrays.asList(1,3,2,5,6,3,5,5,2);

        // 遍历List集合,调用forEach方法
        // forEach方法的参数是一个函数式接口:Consumer (消费型接口)
        // 匿名内部类方式
        list.forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer elt) {
                // elt 代表的就是集合中的每个元素
                System.out.println(elt);
            }
        });

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

        // Lambda表达式改进
        list.forEach(elt -> System.out.println(elt));

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

        // 使用方法引用精简
        list.forEach(System.out::println);

    }
}

运行结果:

*          使用集合提供的forEach方法,结合Lambda表达式。
*          遍历Set集合。
import java.util.TreeSet;

/**
 * ClassName: LambdaTest19
 * Description:
 *          使用集合提供的forEach方法,结合Lambda表达式。
 *          遍历Set集合。
 */
public class LambdaTest19 {
    public static void main(String[] args) {

        TreeSet<Integer> treeSet = new TreeSet<>();

        treeSet.add(100);
        treeSet.add(100);
        treeSet.add(200);
        treeSet.add(300);
        treeSet.add(400);
        treeSet.add(5);

        // 遍历set集合
        treeSet.forEach(System.out::println);
    }
}

运行结果:

*          使用集合提供的forEach方法,结合Lambda表达式。
*          遍历Map集合。
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;

/**
 * ClassName: LambdaTest20
 * Description:
 *          使用集合提供的forEach方法,结合Lambda表达式。
 *          遍历Map集合。
 */
public class LambdaTest20 {
    public static void main(String[] args) {

        Map<Integer, String> map = new HashMap<>();

        map.put(1, "jack");
        map.put(2, "lucy");
        map.put(3, "wangwu");
        map.put(4, "zhangsan");
        map.put(5, "lisi");

        // 遍历
        // 匿名内部类方式
        map.forEach(new BiConsumer<Integer, String>() {
            @Override
            public void accept(Integer key, String value) {
                System.out.println(key + "=" + value);
            }
        });

        // 使用Lambda表达式改进
        map.forEach((k,v) -> System.out.println(k + "=" + v));
    }
}

运行结果:

2.removeIf()方法

在Collection集合中,提供的removeIf()方法的形参为Predicate接口(判断型接口),通过该方法再配合Lambda表达式就可以遍历List和Set集合中的元素。
【示例】删除List集合中的某个元素

*          集合的removeIf方法。结合Lambda表达式,删除符合条件的元素。
*          遍历过程中删除符合条件的元素。
*          使用的函数式接口是:判断型。(返回boolean类型的方法。)
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;

/**
 * ClassName: LambdaTest21
 * Description:
 *          集合的removeIf方法。结合Lambda表达式,删除符合条件的元素。
 *          遍历过程中删除符合条件的元素。
 *          使用的函数式接口是:判断型。(返回boolean类型的方法。)
 */
public class LambdaTest21 {
    public static void main(String[] args) {
        // 创建List集合
        List<String> list = new ArrayList<>();

        list.add("aa");
        list.add("bb");
        list.add("cc");
        list.add("dd");

        // 删除 cc
        // 匿名内部类的方式
        /*list.removeIf(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return "cc".equals(s);
            }
        });*/

        // 使用Lambda表达式方式
        //list.removeIf(s -> "cc".equals(s));

        // 使用方法引用继续精简
        list.removeIf("cc"::equals);

        System.out.println(list);

        // Set集合中的元素在遍历过程中删除符合条件的元素
        Set<String> set = new HashSet<>();

        set.add("100");
        set.add("200");
        set.add("300");
        set.add("400");

        set.removeIf("300"::equals);

        System.out.println(set);
    }
}

运行结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/980090.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

springboot之集成Elasticsearch

目录 二、Elasticsearch 是什么&#xff1f;三、Elasticsearch 安装四、Springboot 集成 Elasticsearch 的方式五、创建项目集成 Elasticsearch 2.创建 Spring Initializr 项目 es &#xff08;3&#xff09;.新建实体类 User&#xff08;4&#xff09;.新建 dao 接口类 UserR…

HBuilderx 插件开发变量名称翻译 ,中文转(小驼峰,大驼峰,下划线,常量,CSS类名)

HBuilderx 插件开发变量名称翻译 &#xff0c;中文转&#xff08;小驼峰&#xff0c;大驼峰&#xff0c;下划线&#xff0c;常量&#xff0c;CSS类名&#xff09; 插件开发文档 工具HBuilderx &#xff0c;创建项目 创建成功后目录 插件需求 开发时 用来将中文转为&#xff0…

C# 数据转换

1. 文本框读取byte&#xff0c;ushort格式数据 byte addr; if (byte.TryParse(textBoxAddr.Text, out addr) true) {}2. 字节数组 (byte[]) 转换为 ASCII 字符串 byte[] bytes { 72, 101, 108, 108, 111 }; // "Hello" 的 ASCII 码 string s0 Encoding.ASCII.Ge…

unity学习60: 滑动条 和 滚动条 滚动区域

目录 1 滚动条 scrollbar 1.1 创建滚动条 1.2 scrollbar的子物体 1.3 scrollbar的属性 2 滚动视图 scroll View 2.1 创建1个scroll View 2.1.1 实际类比&#xff0c;网页就是一个 scroll view吧 2.2 子物体构成 2.3 核心component : Scroll Rect 3 可视区域 view p…

如何通过 LlamaIndex 将数据导入 Elasticsearch

作者&#xff1a;来自 Elastic Andre Luiz 逐步介绍如何使用 RAG 和 LlamaIndex 提取数据并进行搜索。 在本文中&#xff0c;我们将使用 LlamaIndex 来索引数据&#xff0c;从而实现一个常见问题搜索引擎。 Elasticsearch 将作为我们的向量数据库&#xff0c;实现向量搜索&am…

从黑暗到光明:FPC让盲人辅助眼镜成为视障者的生活明灯!【新立电子】

在科技日新月异的今天&#xff0c;智能技术正以前所未有的方式改变着我们的生活。对于视障人士而言&#xff0c;科技的进步更是为他们打开了一扇通往更加独立自主生活的大门。其中&#xff0c;盲人辅助智能眼镜可以成为视障人士日常生活中的得力助手。FPC在AR眼镜中的应用&…

【MySQL】数据类型与表约束

目录 数据类型分类 数值类型 tinyint类型 bit类型 小数类型 字符串类型 日期和时间类型 enum和set 表的约束 空属性 默认值 列描述 zerofill 主键 自增长 唯一键 外键 数据类型分类 数值类型 tinyint类型 MySQL中&#xff0c;整形可以是有符号和无符号的&…

tableau之标靶图、甘特图和瀑布图

一、标靶图 概念 标靶图&#xff08;Bullet Chart&#xff09;是一种用于显示数据与目标之间关系的可视化图表&#xff0c;常用于业务和管理报告中。其设计旨在用来比较实际值与目标值&#xff0c;同时展示额外的上下文信息&#xff08;如趋势&#xff09;。 作用 可视化目标…

微服务学习(2):实现SpringAMQP对RabbitMQ的消息收发

目录 SpringAMQP是什么 为什么采用SpringAMQP SpringAMQP应用 准备springBoot工程 实现消息发送 SpringAMQP是什么 Spring AMQP是Spring框架下用于简化AMQP&#xff08;高级消息队列协议&#xff09;应用开发的一套工具集&#xff0c;主要针对RabbitMQ等消息中间件的集成…

【JAVA SE基础】抽象类和接口

目录 一、前言 二、抽象类 2.1 抽象类的概念 2.2 抽象类语法 2.3 抽象类特性 2.4 抽象类的作用 三、接口 3.1 什么是接口 3.2 语法规则 3.3 接口使用 3.4 接口特性 3.5 实现多接口 3.6 接口间的继承 四、Object类 4.1 获取对象信息&#xff08; toString() &…

《每天读一个JDK源码》之HashMap解读

&#x1f4cc;《每天读一个JDK源码》之HashMap解读 &#x1f517;源码定位&#xff1a;java.util.HashMap&#xff08;建议IDE对照阅读&#xff09; 今天我们来破解Java集合框架中最精妙的艺术品——HashMap&#xff01;它不仅是面试必考题&#xff08;出现率99%&#xff09;&…

蓝牙接近开关模块感应开锁手机靠近解锁支持HID低功耗

ANS-BT101M是安朔科技推出的蓝牙接近开关模块&#xff0c;低功耗ble5.1&#xff0c;采用UART通信接口&#xff0c;实现手机自动无感连接&#xff0c;无需APP&#xff0c;人靠近车门自动开锁&#xff0c;支持苹果、安卓、鸿蒙系统&#xff0c;也可以通过手机手动开锁或上锁&…

[STM32]从零开始的STM32 BSRR、BRR、ODR寄存器讲解

一、前言 学习STM32一阵子以后&#xff0c;相信大家对STM32 GPIO的控制也有一定的了解了。之前在STM32 LED的教程中也教了大家如何使用寄存器以及库函数控制STM32的引脚从而点亮一个LED&#xff0c;之前的寄存器只是作为一个引入&#xff0c;并没有深层次的讲解&#xff0c;在教…

Linux下的网络通信编程

在不同主机之间&#xff0c;进行进程间的通信。 1解决主机之间硬件的互通 2.解决主机之间软件的互通. 3.IP地址&#xff1a;来区分不同的主机&#xff08;软件地址&#xff09; 4.MAC地址&#xff1a;硬件地址 5.端口号&#xff1a;区分同一主机上的不同应用进程 网络协议…

C#高级:结合Linq的SelectMany方法实现笛卡尔积效果

一、笛卡尔积定义 又称直积&#xff0c;表示为X Y&#xff0c;第一个对象是X的成员而第二个对象是Y的所有可能有序对的其中一个成员 二、基础示例 class Program {static void Main(string[] args){try{List<List<string>> input new List<List<string&g…

通信原理速成笔记(信息论及编码)

信息论基础 信息的定义与度量 信息是用来消除不确定性的内容。例如&#xff0c;在猜硬币正反的情境中&#xff0c;结果存在正反两种不确定性&#xff0c;而得知正确结果能消除这种不确定性&#xff0c;此结果即为信息。单个事件的信息量&#xff1a;对于离散信源中的事件xi​&…

MySQL实现文档全文搜索,分词匹配多段落重排展示,知识库搜索原理分享

一、背景 在文档搜索场景中&#xff0c;高效精准的搜索功能至关重要&#xff0c;能提升检索效率&#xff0c;为用户提供精准、快速的信息获取体验&#xff0c;提高工作效率。在文档管理系统里&#xff0c;全文搜索是非常重要的功能之一。随着文档数量增长&#xff0c;如何快速…

力扣hot100——回溯

文章目录 前言55.全排列题目描述思路&#xff1a;DFS回溯code 56.子集题目描述思路&#xff1a;dfs回溯code 57.电话号码的字母组合题目描述思路&#xff1a;DFS回溯code 58.数组总和题目描述题目描述code 59.括号生成题目描述思路&#xff1a;DFS回溯code 60.单词搜索题目描述…

云和恩墨亮相PolarDB开发者大会,与阿里云深化数据库服务合作

2025年2月26日&#xff0c;备受瞩目的阿里云PolarDB开发者大会于北京嘉瑞文化中心盛大举行&#xff0c;众多行业精英齐聚一堂&#xff0c;共襄技术盛会。云和恩墨作为阿里云重要的生态合作伙伴受邀参会。云和恩墨联合创始人兼技术研究院总经理杨廷琨与阿里云智能数据库产品事业…

Vue前端项目构建教程

文章目录 1. 引言2.环境准备2.1 请自行查找资料安装以下开发工具2.2 安装cnpm 3. 创建Vue3项目3.1 安装依赖3.2 项目结构 4. 配置Vue项目4.1 在项目根目录下添加环境变量文件4.2 添加环境变量 5. 常用插件和工具总结 1. 引言 本前端项目将使用Vue3 Vite4构建。 Vue3是目前最…