jdk8新特性----Lambda表达式

一、介绍

1、简介

Java的Lambda表达式Java 8引入的一个特性,它支持函数式编程,允许将函数作为方法的参数或返回值,从而简化了匿名内部类的使用,并提供了对并行编程的更好支持。

可以将Lambda表达式理解为一个匿名函数; Lambda表达式允许将一个函数作为另外一个函数的参数; 我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码作为实参),也可以理解为函数式编程,将一个函数作为参数进行传递

2、优点

Lambda表达式的主要优势包括:

  • 简化匿名内部类的使用,使代码更加简洁明了。
  • 支持函数式编程,允许函数作为第一类对象进行传递和操作。
  • 促进并行编程,因为函数式编程中的纯函数天然具备无副作用的特性,使得在并行编程中更容易实现可靠的多线程和并行处理。
3、用途

Lambda表达式主要用于函数式接口,即只包含一个抽象方法的接口,可以使用@FunctionalInterface注解进行标识。常用场景有:

  • 创建Runnable实例:Runnable runnable = () -> {System.out.println("Hello, Lambda!");};
  • 实现Consumer接口:numbers.forEach(n -> System.out.println(n));
  • 实现Predicate接口:filteredNames = names.stream().filter(name -> name.length() > 5).collect(Collectors.toList());
  • 创建线程:new Thread(() -> System.out.println("haha")).start();

二、语法与使用

1、基本语法
([Lambda参数列表,即形参列表]) -> {Lambda体,即方法体}

  如果主体只有一行代码可以更简化,去掉{}:

(parameters) -> expression

(1)Lambda 表达式关注的是接口中方法的返回值和参数,方法名不重要
(2)使用 "->"将参数和实现逻辑分离;

       ( ) 中的部分是需要传入Lambda体中的参数,参数可以是任意合法的Java参数列表,可以为空或包含一个或多个参数;

        { } 中部分,接收来自 ( ) 中的参数,完成一定的功能。Lambda主体可以是一个表达式,也可以是一个代码块。如果主体是一个表达式,它将直接返回该表达式的结果。如果主体是一个代码块,它将按照常规的Java语法执行,并且您可能需要使用return语句来返回值。
(3)只有函数式接口的变量或者是函数式接口,才能够赋值为Lambda表达式。这个接口中,可以有默认方法,或者是静态方法。

2、使用条件

lambda表达式和方法引用使用前提:函数式接口
(1)@FunctionalInterface 语法格式严格要求当前接口有且只能有一个尚未完成的缺省属性为 public abstract 修饰方法。Lambda表达式的使用前提是存在一个接口,该接口中有且只有一个抽象方法。在主方法中可以通过创建接口的匿名内部类或使用Lambda表达式来调用该接口的方法。
(2)函数式接口一般用于方法的增强,直接作为方法的参数,实现函数式编程。只有函数式接口的变量或者是函数式接口,才能够赋值为Lambda表达式。这个接口中,可以有默认方法,或者是静态方法。

3、四种lambda表达式
(1)无参数无返回值
() -> System.out.println("Hello, Lambda!");

 如:

//接口设计
@FunctionalInterface
interface A {
	void 方法名真的没有用();
}
//方法设计
public static void testLambda(A a) {
    a.方法名真的没有用();
}
//代码实现
public static void main(String[] args) {
	//1.匿名内部类方法
	//接口无法实例化,这里实例化的是 A 接口的实现类对象(该方法在jdk1.8以后被lambda表达式完虐)
    testLambda(new A() {
    	//缺省属性为abstract,需要重写
        @Override
        public void 方法名真的没有用() {
            System.out.println("无参数返回值 匿名内部类对象方法实现");
        }
    });

    /*
    2. Lambda 表达式实现
    【分析】
        void 方法名真的没有用();
        接口方法【返回值类型】 void,无返回值
        接口方法【参数】 无参数
     */
    testLambda(() -> {
        System.out.println("Lambda 表达式初体验");
    });

    //Lambda 表达式有且只有一行代码,可以省略大括号
    testLambda(() -> System.out.println("Lambda 表达式初体验"));
(2) 无参数有返回值
(x, y) -> System.out.println(x + y);




(x, y) -> {
    int sum = x + y;
    System.out.println("Sum: " + sum);
    return sum;
}
//接口设计
@FunctionalInterface
interface Supplier<T> {
    /**
    * 无参数有返回值方法,泛型约束的是接口对应的返回值数据类型,要求按照泛型约束返回对应的数据内容
    *
    * @return 返回一个数据,符合泛型约束
    */
	T get();
}
/*
 * 当前方法要求返回一个字符串数据内容
 */
public static String testLambda(Supplier<String> s) {
    return s.get();
}
public static void main(String[] args) {
     /*
    【分析】
        T get(); ==> 泛型约束为 String ==> String get();
        接口方法【返回值类型】 String
        接口方法【参数】 无参数
     Lambda 格式:() -> {必须返回一个 String 类型}
     */
    String s1 = testLambda(() -> {
        return "这里也是一个字符串";
    });
    System.out.println(s1);

    /*
    Lambda 优化,只要 -> 之后是一个 字符串数据内容就可以满足当前 Lambda 所需
    可以省略 return ,前提是当前 Lambda 有且只有一行代码
     */
    String s2 = testLambda(() -> "这里也是一个字符串");
    System.out.println(s2);

    /*
    Lambda 内部使用使用方法局部变量
     */
    String str = "name=王小明&age=23&country=中国";
    String s3 = testLambda(() -> {
        // str 是当前 main 方法局部变量,Lambda 内部可以直接使用
        String[] split = str.split("&");
        return split[0];
    });
    System.out.println(s3);//返回王小明
}
(3) 有参数无返回值
@FunctionalInterface
interface Consumer<T> {
    /**
    * 消费者接口,数据最终处理接口,数据处理终止方法接口,对应的方法要求方法有参数无返回值
    *
    * @param t 泛型数据数据类型 T ,支持任意类型,在接口约束之后,要求符合数据类型一致化要求
    */
	void accept(T t);
}
/**
 * 有参数无返回 Lambda 测试方法,方法参数是 String 类型和针对于 String 类型
 * 进行数据处理的 Consumer 接口,Consumer 接口可以传入实现类对象和 Lambda 表达式
 *
 * @param str    目标处理的 String 字符串数据
 * @param handle 已经约束为处理 String 类型数据的 Consumer 接口处理器
 */
public static void testLambda(String str, Consumer<String> handle) {
    handle.accept(str);
}
public static void main(String[] args) {
    /*
    1、匿名内部类 Low
     */
    testLambda("孟州市炒面第一名", new Consumer<String>() {
        @Override
        public void accept(String t) {
            System.out.println(t);
        }
    });

    /*
    2. Lambda 表达式
    【分析】
        void accept(T t); ==> 泛型约束为 String ==> void accept(String t);
        接口方法【返回值】 void
        接口方法【参数】 1 个参数,String 类型
     Lambda 格式
        Lambda 小括号中的临时变量名称,没有数据类型体现,需要【联想】目标方法数据类型
        只按照参数的个数定义临时小变量
        (s) -> {大括号中无需返回值类型}
            Lambda 表达式临时变量 s 对应的数据类型为 String 类型 【联想可得】
     */
    testLambda("lambda表达式需要联想!!!", (s) -> {
        System.out.println(Arrays.toString(s.toCharArray()));
    });

    /*
    Lambda 优化
        1. 代码块有且只有一行,可以省略大括号
        2. 小括号中有且只有一个 参数,可以省略小括号

    【注意】
        Lambda 承担的角色是一个针对于 String 字符串的处理器
     */
    testLambda("lambda表达式需要联想!!!", s -> System.out.println(Arrays.toString(s.toCharArray())));
}
(4)有参数有返回值 
4、方法引用(拓展):

当Lambda表达式满足某种条件的时候,使用方法引用,可以再次简化代码。

(1)构造引用

当Lambda表达式是通过new一个对象来完成的,那么可以使用构造引用。

import java.util.function.Supplier;
public class TestLambda {
    public static void main(String[] args) {
//        Supplier<Student> s = () -> new Student();
 
        Supplier<Student> s = Student::new;
    }//实际过程:将new Student()赋值给了Supplier这个函数式接口中的那个抽象方法
}
(2) 类名::实例方法

Lambda表达式的的Lambda体也是通过一个对象的方法完成,但是调用方法的对象是Lambda表达式的参数列表中的一个,剩下的参数正好是给这个方法的实参。

import java.util.TreeSet;
public class TestLambda {
    public static void main(String[] args) {
        TreeSet<String> set = new TreeSet<>((s1,s2) -> s1.compareTo(s2));
}
(3) 对象::实例方法
*/ //类名::实例方法
        TreeSet<String> set = new TreeSet<>(String::compareTo);
        set.add("Hello");
        set.add("isea_you");
       // set.forEach(t -> System.out.println(t));//Hello \n isea_you
        set.forEach(System.out::println);
        //(1)对象::实例方法,Lambda表达式的(形参列表)与实例方法的(实参列表)类型,个数是对应
    }
}
(4)类名::静态方法 
package com.isea.java;
import java.util.stream.Stream;
public class TestLambda {
    public static void main(String[] args) {
//        Stream<Double> stream = Stream.generate(() -> Math.random());
//        类名::静态方法, Lambda表达式的(形参列表)与实例方法的(实参列表)类型,个数是对应
        Stream<Double> stream = Stream.generate(Math::random);
        stream.forEach(System.out::println);
    }
}
5、变量捕获
5.1、匿名内部类的变量捕获

在Java中,匿名内部类可以捕获外部变量,即在匿名内部类中引用并访问外部作用域的变量。这种行为称为变量捕获(Variable Capturing)。在匿名内部类中,可以捕获以下类型的变量:

(1)实例变量(Instance Variables):如果匿名内部类位于一个实例方法中,它可以捕获并访问该实例的实例变量。

(2)静态变量(Static Variables):匿名内部类可以捕获并访问包含它的类的静态变量。

(3)方法参数(Method Parameters):匿名内部类可以捕获并访问包含它的方法的参数。

(4)本地变量(Local Variables):匿名内部类可以捕获并访问声明为final的本地变量。从Java 8开始,final关键字可以省略,但该变量实际上必须是最终的(即不可修改)。

当匿名内部类捕获变量时,它们实际上是在生成的字节码中创建了一个对该变量的副本。这意味着即使在外部作用域中的变量发生改变,匿名内部类中捕获的变量仍然保持其最初的值。demo:

public class OuterClass {
    private int instanceVariable = 10;
    private static int staticVariable = 20;

    public void method() {
        final int localVar = 30; // 或者直接使用 Java 8+ 的隐式 final

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("Instance variable: " + instanceVariable);
                System.out.println("Static variable: " + staticVariable);
                System.out.println("Local variable: " + localVar);
            }
        };

        runnable.run();
    }
}
5.2、Lambda表达式的变量捕获 

在Lambda表达式中,同样可以捕获外部作用域的变量。Lambda表达式可以捕获以下类型的变量:

(1)实例变量(Instance Variables):Lambda表达式可以捕获并访问包含它的实例的实例变量。

(2)静态变量(Static Variables):Lambda表达式可以捕获并访问包含它的类的静态变量。

(3)方法参数(Method Parameters):Lambda表达式可以捕获并访问包含它的方法的参数。

(4)本地变量(Local Variables):Lambda表达式可以捕获并访问声明为final的本地变量。从Java 8开始,final关键字可以省略,但该变量实际上必须是最终的(即不可修改)。

与匿名内部类不同,Lambda表达式不会创建对变量的副本,而是直接访问变量本身。这意味着在Lambda表达式中捕获的变量在外部作用域中发生的改变也会在Lambda表达式中反映出来。demo:

public class LambdaVariableCapture {
    private int instanceVariable = 10;
    private static int staticVariable = 20;

    public void method() {
        int localVar = 30;

        // Lambda表达式捕获外部变量
        Runnable runnable = () -> {
            System.out.println("Instance variable: " + instanceVariable);
            System.out.println("Static variable: " + staticVariable);
            System.out.println("Local variable: " + localVar);
        };

        runnable.run();
    }
}

三、使用场景

1、在集合中的使用

Lambda表达式在Collection接口中的使用主要涉及对集合进行迭代、筛选和转换等操作。在Java 8及以上的版本中,Collection接口增加了一些默认方法,例如forEach()removeIf()stream()等,使得使用Lambda表达式更加方便。

1.1、Collection接口
(1)迭代
    List<String> stringList = Arrays.asList("apppe","organge","banana");
    // 原来的方式
    for (String s : stringList) {
          System.out.println(s);
      }
    // lambda 表达式
    stringList.forEach(s->{
       System.out.println(s);
    });
(2)筛选
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

numbers.removeIf(n -> n % 2 == 0);
(3)转换
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

List<Integer> nameLengths = names.stream()
                                 .map(name -> name.length())
                                 .collect(Collectors.toList());
(4)获取集合流:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

Stream<Integer> stream = numbers.stream();
(5)使用filter()筛选集合元素:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

List<String> filteredNames = names.stream()
                                  .filter(name -> name.startsWith("A"))
                                  .collect(Collectors.toList());
1.2、 List接口
(1)遍历
List<String> fruits = Arrays.asList("Apple", "Banana", "Orange");

fruits.forEach(fruit -> System.out.println(fruit));
//分批新增
Lists.partition(userList, 100)
                .forEach(userListPart -> userMapper.saveBatch(userListPart ));
(2)过滤
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

List<Integer> evenNumbers = numbers.stream()
                                   .filter(number -> number % 2 == 0)
                                   .collect(Collectors.toList());
(3)映射
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

List<Integer> nameLengths = names.stream()
                                 .map(name -> name.length())
                                 .collect(Collectors.toList());
(4)查找
List<String> fruits = Arrays.asList("Apple", "Banana", "Orange");

Optional<String> foundFruit = fruits.stream()
                                   .filter(fruit -> fruit.startsWith("B"))
                                   .findFirst();
(5)排序
List<Integer> numbers = Arrays.asList(5, 2, 8, 1, 6, 3, 9, 4, 7, 10);

List<Integer> sortedNumbers = numbers.stream()
                                     .sorted()
                                     .collect(Collectors.toList());
1.3、Map接口

(1)迭代Map的键值对:

Map<String, Integer> map = new HashMap<>();
map.put("Alice", 25);
map.put("Bob", 30);
map.put("Charlie", 35);

map.forEach((key, value) -> System.out.println(key + ": " + value));

(2)遍历Map的键或值:

Map<String, Integer> map = new HashMap<>();
map.put("Alice", 25);
map.put("Bob", 30);
map.put("Charlie", 35);

map.keySet().forEach(key -> System.out.println(key));
map.values().forEach(value -> System.out.println(value));

(3)使用Stream过滤Map的键值对:

Map<String, Integer> map = new HashMap<>();
map.put("Alice", 25);
map.put("Bob", 30);
map.put("Charlie", 35);

Map<String, Integer> filteredMap = map.entrySet()
                                      .stream()
                                      .filter(entry -> entry.getValue() > 30)
                                      .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
(4)对Map的值进行映射:
Map<String, Integer> map = new HashMap<>();
map.put("Alice", 25);
map.put("Bob", 30);
map.put("Charlie", 35);

Map<String, String> mappedMap = map.entrySet()
                                   .stream()
                                   .collect(Collectors.toMap(Map.Entry::getKey, entry -> "Age: " + entry.getValue()));
(5)对Map的键或值进行归约操作:
Map<String, Integer> map = new HashMap<>();
map.put("Alice", 25);
map.put("Bob", 30);
map.put("Charlie", 35);

int sumOfValues = map.values().stream().reduce(0, (a, b) -> a + b);
String concatenatedKeys = map.keySet().stream().reduce("", (a, b) -> a + b);

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

w_t_y_y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值