Java新特性:Lambda表达式

news2024/11/18 8:39:33

Java新特性:Lambda表达式

Lambda 表达式(Lambda expression),也可称为闭包(Closure),是 Java(SE)8 中一个重要的新特性。Lambda 表达式允许我们通过表达式来代替功能接口。Lambda 表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块)

Lambda 表达式可以看作是一个匿名函数,基于数学中的 λ 演算得名。


文章目录

      • Java新特性:Lambda表达式
        • 1、Lambda表达式概述
          • 1.1、Lambda表达式的简介
          • 1.2、Lambda表达式的语法
          • 1.3、Lambda表达式的要求
        • 2、函数式接口
          • 2.1、什么是函数式接口
          • 2.2、函数式接口注解(@FunctionalInterface)
        • 3、Lambda表达式的使用
          • 3.1、Lambda表达式的基本使用
          • 3.2、Lambda表达式的语法精简
        • 4、变量捕获
          • 4.1、匿名内部类的变量捕获
          • 4.2、Lambda的变量捕获
          • 4.3、匿名内部类的变量捕获与Lambda的变量捕获的区别
        • 5、Lambda 在集合当中的使用
          • 5.1、Collection接口中的forEach()方法
          • 5.2、List接口中的sort()方法
          • 5.3、Map接口的forEach()方法


1、Lambda表达式概述

1.1、Lambda表达式的简介

Lambda 表达式(Lambda expression),也可称为闭包(Closure),是 Java(SE)8 中一个重要的新特性。

Lambda 表达式允许我们通过表达式来代替功能接口。Lambda 表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块)

Lambda 表达式可以看作是一个匿名函数,基于数学中的 λ 演算得名。

1.2、Lambda表达式的语法

Lambda 表达式的基本语法:

(parameters) -> expression 或 (parameters) -> { statements; }

Lambda 表达式的三个组成部分:

image-20230707105839045

  1. 参数列表(parameters):类似方法中的形参列表,这里的参数是函数式接口里的参数。这里的参数类型可以明确的声明,也可不声明而由 JVM 隐含的推断。另外当只有一个推断类型时可以省略掉圆括号;
  2. 箭头(->):连接参数列表和 Lambda 主体,可理解为"被用于"的意思;
  3. Lambda 主体(expression 或 { statements; }):可以是表达式也可以代码块,是函数式接口里方法的实现。代码块可返回一个值或者什么都不反回,这里的代码块块等同于方法的方法体。如果是表达式,也可以返回一个值或者什么都不反回。

Lambda 表达式样例:

        // 1. 不需要参数,返回值为 2
        () -> 2
				// 2. 接收一个参数(数字类型),返回其2倍的值
        x -> 2*x
				// 3. 接受2个参数(数字),并返回他们的和
        (x, y) -> x+y
				// 4. 接收2个int型整数,返回他们的乘积
        (int x, int y) -> x*y
				// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
        (String s) -> System.out.print(s)
1.3、Lambda表达式的要求

虽然说,Lambda 表达式可以在⼀定程度上简化接口的实现。但是,并不是所有的接口都可以使用 Lambda 表达式来简洁实现的。

Lambda 表达式毕竟只是⼀个匿名方法。当实现的接口中的方法过多或者多少的时候,Lambda 表达式都是不适用的。

Lambda 表达式,只能实现函数式接口。


2、函数式接口

2.1、什么是函数式接口

如果说,⼀个接口中,要求实现类必须实现的抽象方法,有且只有⼀个!这样的接口,就是函数式接口。

代码如下(示例):

// 有且只有一个实现类必须要实现的抽象方法,所以是函数式接口
interface Demo{
    public void action();
}

或者(示例):

// 有且只有一个实现类必须要实现的抽象方法,所以是函数式接口
interface Demo{
    public void action();
    public default void action2() {
        System.out.println("JDK1.8新特性,default默认方法可以有具体的实现");
    }
}
2.2、函数式接口注解(@FunctionalInterface)

在接口之前,我们可以使用 @FunctionalInterface 注解来判断这个接口是否是⼀个函数式接口。,用如果是函数式接口,没有任何问题。如果不是函数式接口,则会报错。功能类似于 @Override
代码如下(示例):

@FunctionalInterface
interface Demo{
    public void action();
}

3、Lambda表达式的使用

3.1、Lambda表达式的基本使用

例1(无参数无返回值):

@FunctionalInterface
interface NoParameterNoReturn {
    // 注意:只能有一个抽象方法
    void action();
}
public class TestDemo {
    public static void main(String[] args) {
        NoParameterNoReturn noParameterNoReturn = () -> {
            System.out.println("无参数无返回值");
        };
        // action方法的主体内容在上述括号内
        noParameterNoReturn.action(); 
     }
}

例2(一参数无返回值):

@FunctionalInterface
interface OneParameterNoReturn {
  	// 注意:只能有一个抽象方法
    void action(int a);
}
public class TestDemo {
    public static void main(String[] args) {
        OneParameterNoReturn oneParameterNoReturn = (int a) -> {
            System.out.println("一参数无返回值:" + "参数1:" + a);
        };
        // action方法的主体内容在上述括号内
        oneParameterNoReturn.action(10);
     }
}

例3(多参数无返回值):

@FunctionalInterface
interface MoreParameterNoReturn {
  	// 注意:只能有一个抽象方法
    void action(int a, int b);
}
public class TestDemo {
    public static void main(String[] args) {
						MoreParameterNoReturn moreParameterNoReturn = (int a, int b) -> {
            System.out.println("多参数无返回值:" + "参数1:" + a  + "参数2:" + b);
        };
        moreParameterNoReturn.action(20, 30);
     }
}

例4(无参数有返回值):

@FunctionalInterface
interface NoParameterReturn {
    int action();
}
public class TestDemo {
    public static void main(String[] args) {
        NoParameterReturn noParameterReturn = () -> {
            System.out.println("无参数有返回值");
            return 40;
        };
        //接收函数的返回值
        int ret = noParameterReturn.action();
        System.out.println(ret);
     }
}

例5(一参数有返回值):

//有返回值一个参数
@FunctionalInterface
interface OneParameterReturn {
    int action(int a);
}
public class TestDemo {
    public static void main(String[] args) {
        OneParameterReturn oneParameterReturn = (int a) -> {
            System.out.println("一参数有返回值");
            return a;
        };
        ret = oneParameterReturn.action(50);
        System.out.println(ret);
     }
}

例6(多参数有返回值):

@FunctionalInterface
interface MoreParameterReturn {
    int action(int a, int b);
}
 
public class TestDemo {
    public static void main(String[] args) {
        MoreParameterReturn moreParameterReturn = (int a, int b) -> {
            System.out.println("多参数有返回值");
            return a + b;
        };
        ret = moreParameterReturn.action(60, 70);
        System.out.println(ret);
     }
}
3.2、Lambda表达式的语法精简

Lambda 表达式的语法精简:

  • 参数类型可以省略,如果需要省略,每个参数的类型都要省略;
  • 参数的小括号里面只有一个参数,那么小括号可以省略;
  • 如果方法体当中只有一句代码,那么大括号可以省略;
  • 如果方法体中只有一条语句,其是 return 语句,那么大括号可以省略,且去掉 return 关键字。

4、变量捕获

Lambda 表达式中存在变量捕获,了解了变量捕获之后,我们才能更好的理解 Lambda 表达式的作用域。Java 当中的匿名类中,会存在变量捕获。

4.1、匿名内部类的变量捕获

所谓变量捕获就是:匿名内部类中访问所在方法或代码块中的局部变量,这个过程被称为 “变量捕获”。

public static void main(String[] args) {
    // 变量捕获
    int a = 100;
    NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn() {
        @Override
        public void action() {
            // 捕获a变量
            System.out.println(a);   
        }
    };
    noParameterNoReturn.action();
}
结果:100

但是,只有在局部变量是 “有效的最终变量” 时,匿名内部类才能够引用它。如果局部变量不是有效的最终变量,则无法在匿名内部类中引用它,会导致编译错误。

有效的最终变量指的是一个在生命周期中没有被修改过的局部变量,它可以被认为是一个常量。

4.2、Lambda的变量捕获

同样的,Lambda 的变量捕获也是只有在局部变量是 “有效的最终变量” 时,才能够引用它。

public static void main(String[] args) {
    // 变量捕获
    int a = 100;
    NoParameterNoReturn noParameterNoReturn = ()->{
        // 捕获a变量
        System.out.println(a);
    };
    noParameterNoReturn.action();
}
结果:100
4.3、匿名内部类的变量捕获与Lambda的变量捕获的区别

区别:Lambda 表达式可以捕获外面的 this,而匿名内部类无法直接捕获外面的 this。

在 Lambda 表达式中,this 关键字引用的是 Lambda 表达式所在的类的实例,而不是 Lambda 表达式内部的类的实例。这个特性被称为 “闭包”。

匿名内部类中的 this 关键字引用的是匿名内部类的实例,而不是外部类的实例。

public class Student {
    // 外部类的属性
    String name = "Student";
    int age;
 
    NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn() {
        // 内部类自己的属性
        String name = "hello";
        @Override
        public void action() {
            System.out.println(this.name);
        }
    };
 
    NoParameterNoReturn noParameterNoReturn2 = ()->{
        String name = "hello";
        System.out.println(this.name);
    };
 
    public static void main(String[] args) {
        Student student = new Student();
      	// 结果:hello
        student.noParameterNoReturn.action();
      	// 结果:Student
        student.noParameterNoReturn2.action();
    }
}

如果想让匿名内部类的捕获外面的 this,就需要使用外部类的类名或对象引用来访问。

public class Student {
    // 外部类的属性
    String name = "Student";
    int age;
 
     NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn() {
        // 内部类自己的属性
        String name = "hello";
        @Override
        public void action() {
            System.out.println(Student.this.name);
        }
    };
}
结果:Student

5、Lambda 在集合当中的使用

为了能够让 Lambda 和 Java 的集合类集更好的一起使用,集合当中,也新增了部分接口,以便与 Lambda 表达式对接。

对应的接口新增的方法
CollectionforEach() removeIf() spliterator() stream() parallelStream()
ListreplaceAll() sort()
MapgetOrDefault() forEach() replaceAll() putIfAbsent() remove() replace() computeIfAbsent() computeIfPresent() compute() merge()
5.1、Collection接口中的forEach()方法

这个方法是在接口 Iterable 当中的:

    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

这个方法表示用于对集合中的每个元素执行指定的操作。

java复制代码public class Test {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("bit");
        list.add("hello");
        list.add("lambda");
        
        // Lambda 遍历 list
        list.forEach((String s)->{
            System.out.println(s);
        });
      
      	// 简化
      	// list.forEach(s -> System.out.println(s));
    }
}
结果:Hello bit hello lambda
5.2、List接口中的sort()方法

List 接口中的 sort() 方法:

    default void sort(Comparator<? super E> c) {
        Object[] a = this.toArray();
        Arrays.sort(a, (Comparator) c);
        ListIterator<E> i = this.listIterator();
        for (Object e : a) {
            i.next();
            i.set((E) e);
        }
    }

该方法根据 c 指定的比较规则对容器元素进行排序。可以看到其参数是 Comparator,我们点进去看下:又是一个函数式接口:

@FunctionalInterface
public interface Comparator<T> {
		...
    int compare(T o1, T o2);
    ...
}

使用示例:

public class TestDemo2 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("bit");
        list.add("hello");
        list.add("lambda");

        /*
        对list集合中的字符串按照长度进行排序
         */
        list.sort(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.length() - o2.length();
            }
        });

        /*
        输出排序后最终的结果
         */
        list.forEach(s -> System.out.println(s));
    }
}
输出结果为:bit Hello hello lambda

修改为 Lambda 表达式:

public class TestDemo2 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("bit");
        list.add("hello");
        list.add("lambda");

        // 对list集合中的字符串按照长度进行排序
        list.sort((o1, o2) -> o1.length() - o2.length());
        list.forEach(s -> System.out.println(s));
    }
}
输出结果为:bit Hello hello lambda
5.3、Map接口的forEach()方法

HashMap 的 forEach() 方法:

    default void forEach(BiConsumer<? super K, ? super V> action) {
        Objects.requireNonNull(action);
        for (Map.Entry<K, V> entry : entrySet()) {
            K k;
            V v;
            try {
                k = entry.getKey();
                v = entry.getValue();
            } catch(IllegalStateException ise) {
                // this usually means the entry is no longer in the map.
                throw new ConcurrentModificationException(ise);
            }
            action.accept(k, v);
        }
    }

可以看到这个方法有一个 BigConsumer,并且这也是一个函数式接口:

@FunctionalInterface
public interface BiConsumer<T, U> {
		...
    void accept(T t, U u);
    ...
}

代码示例:

public class TestDemo2 {
    public static void main(String[] args) {
        HashMap<Integer, String> map = new HashMap<>();
        map.put(1, "hello");
        map.put(2, "bit");
        map.put(3, "hello");
        map.put(4, "lambda");

        map.forEach(new BiConsumer<Integer, String>() {
            @Override
            public void accept(Integer integer, String string) {
                System.out.println(integer + " " + string);
            }
        });

    }
}
输出结果:
1 hello
2 bit
3 hello
4 lambda

修改为 Lambda 表达式:

public class TestDemo2 {
    public static void main(String[] args) {
        HashMap<Integer, String> map = new HashMap<>();
        map.put(1, "hello");
        map.put(2, "bit");
        map.put(3, "hello");
        map.put(4, "lambda");

        map.forEach((integer, string) ->
                System.out.println(integer + " " + string)
        );
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第五章——循环和关系表达式

for循环 很多情况下都需要程序执行重复的任务 #include<iostream> using namespace std; int main() {int i;for (i 0; i < 5; i){cout << "C knows loop.\n";}cout << "C knows when to stop.\n";return 0; } for循环的组成部分 …

107、基于51单片机多路无线调频对讲机系统设计(程序+原理图+PCB源文件+参考论文+外文翻译+任务书+开题报告+硬件设计资料+元器件清单等)

摘 要 对讲机作为短距离通信和移动调度指挥的重要工具,在社会各个行业都有广泛的应用。尤其是随着数字电路技术的发展&#xff0c;新型的对讲机无论在外型还是性能上相对传统的模拟对讲机都有了长足的进步。对讲机主要包含需要基站支持的集群对讲机和常规无中心对讲机两种&…

深度理解 JAVA序列化

前言 相信大家日常开发中&#xff0c;经常看到Java对象“implements Serializable”。那么&#xff0c;它到底有什么用呢&#xff1f;本文从以下几个角度来解析序列这一块知识点~ 什么是Java序列化&#xff1f;为什么需要序列化&#xff1f;序列化用途Java序列化常用API序列化…

Android 热修复一

一、什么是热修复&#xff1f; 在我们应用上线后出现bug需要及时修复时&#xff0c;不用再发新的安装包&#xff0c;只需要发布补丁包&#xff0c;在客户无感知下修复掉bug。 实现效果&#xff1a; Demo源码&#xff1a; https://gitee.com/sziitjim/hotfix 二、怎么进行热修…

极速上手k8s,Kubernetes 从入门到摸鱼系列-理论篇

1. 引言&#x1f44b; 大家好&#xff0c;我是比特桃&#xff01;随着微服务架构越来越流行&#xff0c;大规模的微服务容器编排成了一件具有挑战的事情。在这次容器化云原生的发展中&#xff0c;Docker 成了容器化的赢家&#xff0c;而 Kubernetes 则成为了容器编排的赢家。k…

「已解决」 模块““umi“” ““@umijs/max“” 没有导出的成员“useRequest” “request” 问题的所有方法汇总

背景 使用 Umi 搭建项目时候有的时候会出现这种错误&#xff0c;模块““umi”” ““umijs/max”” 没有导出的成员“useRequest” “request”。 解决 tsconfig.json "paths": {"/*": ["src/*"],"/*": ["./src/.umi/*"…

用JShaman本地部署版,加密2.7MB的Webpack生成的JS文件

JShaman是知名的JS代码保护平台。在线使用&#xff0c;一键混淆加密&#xff0c;无需注册、无需登录。可免费用&#xff0c;也有商业服务&#xff1b;有在线使用的SAAS平台网站&#xff0c;也有本地部署版。很方便、很强大&#xff0c;很专业。 今天&#xff0c;测试使用JSham…

unordered_map模拟实现|STL源码剖析系列|开散列

博主很久没有更新过STL源码剖析这个系列的文章了&#xff0c;主要是因为大部分STL常用的容器&#xff0c;博主都已经发过文章了&#xff0c;今天博主带着大家把哈希表也模拟实现一下。 前言 那么这里博主先安利一下一些干货满满的专栏啦&#xff01; 手撕数据结构https://blo…

点云最小外包矩形计算

1、原理介绍 一簇点云的最小外包矩形&#xff08;Minimum Bounding Rectangle&#xff0c;MBR&#xff09;&#xff0c;是指用一个矩形将该簇点云框起来&#xff0c;所有点云数据在矩形框内。如下图所示为一个矩形框刚好将点云数据全部包围。 下面给出一种基于最大重叠度的最小…

[工业互联-19]:如何在QT中增加SOEM主站

目录 第1章 基本步骤 第2章 详细步骤 2.1.QT安装 2.2.VS安装 2.3.Win10 Debuggers 2.4.QT配置 2.5. SOEM移植 &#xff08;&#xff11;&#xff09;lib库生成 &#xff08;2&#xff09;文件移植: 文件整理 第1章 基本步骤 要在QT中添加SOEM主站功能&#xff0c;您需…

用OpenCV创建一张灰度黑色图像并设置某一列为白色

这段代码首先创建了一个400行600列的单通道灰度图像。然后,它遍历图像中的每个像素。如果像素位于列索引为30的列中,则将该像素的值设置为255。在灰度图像中,0表示黑色,255表示白色。因此,这段代码将图像的第30列设置为白色。 在 OpenCV 中,cv::Mat 构造函数的调用 cv::…

【算法 -- LeetCode】(13)罗马数字转整数

1、题目 罗马数字包含以下七种字符&#xff1a; I&#xff0c; V&#xff0c; X&#xff0c; L&#xff0c;C&#xff0c;D 和 M。 字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M …

哈希表和字符串专题1—205. 同构字符串 1002. 查找共用字符 925. 长按键入 844.比较含退格的字符串 C++实现

文章目录 205. 同构字符串1002. 查找共用字符925. 长按键入844.比较含退格的字符串栈模拟双指针 205. 同构字符串 class Solution { public:bool isIsomorphic(string s, string t) {unordered_map<char, char> map1;unordered_map<char, char> map2;for(int i0, j…

AI绘画:StableDiffusion炼丹Lora攻略-实战萌宠图片生成

Lora攻略-实战萌宠图片生成 写在前面的话一&#xff1a;准备二、Lora作用1.AI模特2.炼衣服Lora3.改变画风/画面背景Lora模型究竟是什么&#xff1f; 三、如何炼制自己的Lora模型&#xff1f;四、炼丹前的准备&#xff08;**下载整合包**&#xff09;五、选择合适的大模型六、高…

管理类联考——逻辑——记忆篇——数字编码——公式

&#x1f3e0;个人主页&#xff1a;fo安方的博客✨ &#x1f482;个人简历&#xff1a;大家好&#xff0c;我是fo安方&#xff0c;考取过HCIE Cloud Computing、CCIE Security、CISP、RHCE、CCNP RS、PEST 3等证书。&#x1f433; &#x1f495;兴趣爱好&#xff1a;b站天天刷&…

MySQL练习题(2)

创建如下员工标表 插入数据 1-- 按员工编号升序排列不在10号部门工作的员工信息 2-- 查询姓名第二个字母不是A且薪水大于1000元的员工信息&#xff0c;按薪水降序排列 4-- 求每个部门的平均薪水 5-- 求每个部门的最高薪水 6-- 求每个部门…

Coverity 2021.9 for win Coverity 2022.6 for linux

Coverity是一款快速、准确且高度可扩展的静态分析 (SAST) 解决方案&#xff0c;可帮助开发和安全团队在软件开发生命周期 (SDLC) 的早期解决安全和质量缺陷&#xff0c;跟踪和管理整个应用组合的风险&#xff0c;并确保符合安全和编码标准。Coverity 是一款精确的综合静态分析与…

JVM04-优化JVM内存分配以及内存持续上升问题和CPU过高问题排查

1-JVM内存分配 1.1-JVM内存分配性能问题 JVM内存分配不合理最直接的表现就是频繁的GC&#xff0c;这会导致上下文切换等性能问题&#xff0c;从而降低系统的吞吐量、增加系统的响应时间。因此&#xff0c;如果你在线上环境或性能测试时&#xff0c;发现频繁的GC&#xff0c;且…

密码学入门——分组密码模式

文章目录 参考书一、简介二、ECB模式三、CBC模式四、CFB模式五、OFB模式六、CTR模式 参考书 图解密码技术&#xff0c;第三版 一、简介 分组密码工作模式指的是将明文分成固定长度的块&#xff0c;并对每个块应用相同的加密算法进行加密的过程。这些块的长度通常是64位或128…

leetcode 654. 最大二叉树

2023.7.9 又是一道递归构造二叉树的题&#xff0c;和昨天做的那道题从中序与后序遍历序列构造二叉树类似&#xff0c;5分钟AC了。 大致思路就是通过找到数组中的最大值&#xff0c;并将其作为根节点&#xff0c;然后递归地构建左子树和右子树&#xff0c;最终返回整个最大二叉树…