Lambda表达式超详解

news2024/10/2 16:22:29

目录

背景

Lambda表达式的用法

函数式接口

Lambda表达式的基本使用

语法精简

 变量捕获

匿名内部类

匿名内部类中的变量捕获

Lambda的变量捕获

Lambda表达式在类集中的使用

Collection接口

 List接口

Map接口

总结


背景

Lambda表达式是Java SE 8中的一个重要的新特性.lambda表达式允许你通过表达式来代替功能接口.lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块).Lambda表达式基于数学中的λ演算得名,也可以称为闭包.

Lambda表达式的用法

基本语法:(parameters)->expression或(parameters)->{statements;}

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

1.parameters:类似方法中的形参列表,这里的参数是函数式接口里的参数.这里的参数类型可以明确声明也可不声明而由JVM隐含的判断.另外当只有一个推断类型时可以省略掉圆括号.

2.->:可理解为被用于的意思

3.方法体:可以是表达式也可以代码块,是在函数式接口里方法的实现.代码块可返回一个值或什么都不返回,这里的代码块等同于方法中的方法体,如果是表达式,可以返回一个值或者什么都不返回.

举个栗子:

//1.不需要参数,返回值为2

()->2

//2.接收一个参数(数字类型),返回其两倍的值

x -> 2 * x

//3.接收两个参数(数字),并返回它们的和

(x, y) -> x + y

//4.接收2个int型整数,返回它们的乘积

(int x, int y) -> x * y

//5.接收一个string对象,并在控制台打印,不返回任何值(看起来是返回void)

(String s) -> System.out.println(s)

函数式接口

要了解Lambda表达式,首先需要了解什么是函数式接口,函数接口定义:一个接口有且只有一个抽象方法.

注意:

1.如果一个接口有且只有一个抽象方法,那么该接口就是函数式接口

2.如果我们在某个接口上声明了@FunctionalInterface注解,那么编译器就会按照函数式接口的定义来要求该接口,这样如果有两个抽象方法,程序编译就会报错的.所以,从某种意义上来说,只要你保证你的接口中只有一个抽象方法,你可以不加这个注解.加上就会自动进行检测的.

定义方式:

@FunctionalInterface
interface NoParameterNoReturn {
    //注意:只能有一个方法
    void test();
}

但是这种方法也是可以的:

@FunctionalInterface
interface NoParameterNoReturn {
    void test();
    default void test2() {
        System.out.println("JDK1.8新特性,default默认方法可以有具体的实现");
    }
}

Lambda表达式的基本使用

首先,我们事先准备好几个接口:

//无返回值无参数
@FunctionalInterface
interface NoParameterNoReturn {
    void test();
}

//无返回值一个参数
@FunctionalInterface
interface OneParameterNoReturn {
    void test(int a);
}

//无返回值多个参数
@FunctionalInterface
interface MoreParameterNoReturn {
    void test(int a, int b);
}

//有返回值无参数
@FunctionalInterface
interface NoParameterReturn {
    int test();
}

//有返回值一个参数
@FunctionalInterface
interface OneParameterReturn {
    int test(int a);
}

//有返回值多参数
@FunctionalInterface
interface MoreParameterReturn {
    int test(int a, int b);
}

我们在上面提到过,Lambda可以理解为:Lambda就是匿名内部类的简化,实际上是创建了一个类,实现了接口,重写了接口的方法.

没有使用lambda表达式时的调用方式:

NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn(){
    @Override
    public void test() {
        System.out.println("hello");
    }
};

具体使用见以下代码:

public class TestDemo {
    public static void main(String[] args) {
        NoParameterNoReturn noParameterNoReturn = () -> {
            System.out.println("无参数无返回值");
        };
        noParameterNoReturn.test();

        OneParameterNoReturn oneParameterNoReturn = (int a) -> {
            System.out.println("一个参数一个返回值: " + a);
        };
        oneParameterNoReturn.test(10);

        MoreParameterNoReturn moreParameterNoReturn = (int a, int b) -> {
            System.out.println("多个参数无返回值: " + a + " " + b);
        };
        moreParameterNoReturn.test(20, 30);

        NoParameterReturn noParameterReturn = () -> {
            System.out.println("有返回值无参数! ");
            return 40;
        };
        //接收函数的返回值
        int ret = noParameterReturn.test();
        System.out.println(ret);

        OneParameterReturn oneParameterReturn = (int a) -> {
            System.out.println("有返回值且有一个参数");
            return a;
        };
        ret = oneParameterReturn.test(50);
        System.out.println(ret);

        MoreParameterReturn moreParameterReturn = (int a, int b) -> {
            System.out.println("有返回值且有多个参数");
            return a + b;
        };
        ret = moreParameterReturn.test(70, 60);
        System.out.println(ret);
    }
}

语法精简

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

2.参数的小括号里面只有一个参数,那么小括号可以省略.

3.如果方法体中只有一句代码,那么大括号可以省略.

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

 示例代码:

public static void main(String[] args) {
        MoreParameterNoReturn moreParameterNoReturn = (a, b) -> {
            System.out.println("无返回值多个参数,省略参数类型: " + a + " " + b);
        };
        moreParameterNoReturn.test(20, 30);

        OneParameterNoReturn oneParameterNoReturn = a -> {
            System.out.println("无参数一个返回值, 小括号可以省略: " + a);
        };
        oneParameterNoReturn.test(10);

        NoParameterNoReturn noParameterNoReturn = () -> System.out.println("无参数无返回值,方法体中只有一行代码");
        noParameterNoReturn.test();

        //方法体中只有一条语句,且是return语句
        NoParameterReturn noParameterReturn = () -> 40;
        int ret = noParameterReturn.test();
        System.out.println(ret);
    }

 变量捕获

Lambda表达式中存在标量捕获,了解变量捕获之后,我们才能更好地理解Lambda表达式的作用域.

Java当中的匿名类中,会存在变量捕获.

匿名内部类

匿名内部类就是没有名字的内部类.我们这里只是为了说明变量捕获,所以,匿名内部类只要会使用即可,那么下面来简单看看匿名内部类的使用就好了.

举个简单的例子:

class Test {
    public void func() {
        System.out.println("func()");
    }
}

public class TestDemo {
    public static void main(String[] args) {
        Test t = new Test() {
            @Override
            public void func() {
                System.out.println("我是匿名内部类,且重写了func这个方法! ");
            }
        };

        t.func();
    }
}

在上述代码当中的main函数当中,我们看到的就是一个匿名内部类的简单使用.

匿名内部类中的变量捕获

在匿名内部类中,在使用内外的变量时,有两个条件:(要么是常量,要么是未修改的变量

来看一下代码:

public class TestDemo1 {
    public static void main(String[] args) {
        int a = 100;
        Test t1 = new Test() {
            @Override
            public void func() {
                System.out.println("我是匿名内部类,且重写了func这个方法!");
                System.out.println("我是捕获到变量a==" + a + "我是一个常量, 或者是一个没有改变过值的变量! ");
            }
        };

        t1.func();
    }
}

在上述代码当中变量a就是捕获的变量.这个变量要么是被final修饰,如果不是被final修饰的 你要保证在使用之前没有修改.如下代码就是错误的代码.

 

public class TestDemo2 {
    public static void main(String[] args) {
        int a = 100;
        Test t2 = new Test() {
            @Override
            public void func() {
                a = 99;
                System.out.println("我是匿名内部类,且重写了func这个方法!");
                System.out.println("我是捕获到变量a==" + a + "我是一个常量, 或者是一个没有改变过值的变量! ");
            }
        };
    }
}

该代码直接编译报错.

Lambda的变量捕获

lambda其实就是可以认为是匿名内部类的实现

在lambda表达式中也可以进行变量的捕获,具体我们看一下代码.

@FunctionalInterface
interface NoParameterNoReturn {
    void test();
}

public class TestDemo3 {
    public static void main(String[] args) {
        int a = 10;
        NoParameterNoReturn noParameterNoReturn = () -> {
            //a = 99;error
            System.out.println("捕获变量" + a);
        };
        noParameterNoReturn.test();
    }
}

Lambda表达式在类集中的使用

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

对应的接口新增的方法
Collectionremovelf() spliterator() stream() parallelStream() forEach()
ListreplaceAll() sort()
Map

getOrDefault() forEach() replaceAll() putlfAbsent() remove() replace()

computeIfAbsent() computeIfPresent() compute() merge()

以上方法的作用可自行查看javaapi.

Collection接口

forEach()方法演示

该方法在接口Iterable当中,原型如下:

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

该方法表示:对容器中的每个元素执行action指定动作.

public class TestDemo4 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("bit");
        list.add("hello");
        list.add("lambda");
        list.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                //简单遍历集合中元素
                System.out.println(s + " ");
            }
        });
    }
}

 输出结果:

我们可以修改为如下代码:

public class TestDemo4 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("bit");
        list.add("hello");
        list.add("lambda");
        //表示调用一个,不带有参数的方法,其执行花括号内的语句,为原来的函数体内容.
        list.forEach(s -> System.out.println(s));
    }
}

 List接口

sort()方法的演示

sort()方法源码:该方法根据c指定的比较规则对容器元素进行排序.

public void sort(Comparator<? super E> c) {
    final int expectedModCount = modCount;
    Arrays.sort((E[]) elementData, 0, size, c);
    if(modCount != expectedModCount) {
        throw new ConcurrentModificationExpection();
    }
    modCount++;
}

使用示例:

public class TestDemo5 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("hello");
        list.add("bit");
        list.add("lambda");
        System.out.println(list);
        list.sort(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                //这里比较长度
                return o1.length() - o2.length();
            }
        });
        System.out.println(list);
    }
}

输出结果:

 

修改为lambda表达式:

public class TestDemo5 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("hello");
        list.add("bit");
        list.add("lambda");
        System.out.println(list);
        //调用带有两个参数的方法,且返回长度的差值
        list.sort((str1, str2) -> str1.length() - str2.length());
        System.out.println(list);
    }
}

Map接口

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 {
            //this usually means the entry is no longer in the map.
            throw new ConcurrentModificationExpection(ise);
        }
        action.accept(k, v);
    }
}

作用是对Map中的每个映射执行action指定操作.

代码示例:

public class TestDemo6 {
    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 s) {
                System.out.println(integer + " = " + s);
            }
        });
    }
}

输出结果:

 

使用lambda表达式之后的代码:

public class TestDemo6 {
    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((k, v) -> System.out.println(k + " = " + v));
    }
}

总结

lambda表达式的优点很明显,在代码层次上来说,使代码变得非常地简洁.缺点也不明显,代码不易读.

优点:

1.代码简洁,开发迅速

2.方便函数式编程

3.非常容易进行并行运算

4.Java引入了Lambda,改善了集合操作 

缺点:

1.代码可读性变差

2.在非并行计算中,很多计算未有传统的for性能要高

3.不容易进行调试 

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

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

相关文章

swing快速入门(二十七)

注释很详细&#xff0c;直接上代码 上一篇 新增内容 1.为按钮指定图标 2. 列表框的并列 3.菜单项绑定快捷键 4.控件悬浮提示信息 5.菜单项设置小图标 6.五种布局风格右键选择切换 package swing21_30;import javax.swing.*; import java.awt.*; import java.awt.event.…

JBoss 5.x/6.x 反序列化 CVE-2017-12149 已亲自复现

JBoss 5.x/6.x 反序列化 CVE-2017-12149 已亲自复现 漏洞名称漏洞描述影响版本 漏洞复现环境搭建漏洞利用 修复建议总结 漏洞名称 漏洞描述 2017年8月30日&#xff0c;厂商Redhat发布了一个JBOSSAS 5.x 的反序列化远程代码执行漏洞通告。该漏洞位于JBoss的HttpInvoker组件中的…

HALCONVS .net混合编程案例【02】:检测芯片的管脚间距

前言&#xff1a; 本章给出的HALCON和Visual Studio的WPF项目中联合编程的案例。 VS中WPF的编程&#xff0c;UI的设计非常方便&#xff0c;如果结合HALCON的算法&#xff0c;可以很快实施机器视觉相关的应用。本例的应用&#xff0c;实现了自动检测的一个三洋的芯片的管脚的间…

实验五 用户进程管理-实验部分

目录 一、知识点 1.Unix进程创建API 1.1.创建新进程API 1.2.fork()地址空间的复制 1.4.shell中调用fork()图示 1.5.fork()创建进程个数示例 1.6.fork()与exec()的比较 2.实验执行流程概述 3.创建用户进程 3.1.应用程序的组成和编译 3.2.用户进程的虚拟地址空间 3.3…

初识QT(上篇):What Qt

初识QT&#xff08;上篇&#xff09;&#xff1a;What Qt 前言 & 说明前言说明 初识QT1.1 QT的what1. 介绍2. 发展历程3. QT架构的主要内容4.QT的常用模块 1.2 QT的 why1. QT的核心机制 下篇笔记链接 前言 & 说明 前言 前言&#xff1a; 之前说要share的qt相关知识&am…

Hadoop入门学习笔记——五、在虚拟机中部署Hive

视频课程地址&#xff1a;https://www.bilibili.com/video/BV1WY4y197g7 课程资料链接&#xff1a;https://pan.baidu.com/s/15KpnWeKpvExpKmOC8xjmtQ?pwd5ay8 Hadoop入门学习笔记&#xff08;汇总&#xff09; 目录 五、在虚拟机中部署Hive5.1. 在node1虚拟机安装MySQL5.2.…

正餐---二叉树的OJ题

目录​​​​​​​ 前言&#x1f36f; 1. 检查两颗树是否相同&#x1f947; 1.1 思路分析&#x1fa99; 1.2 代码实现&#x1f9f0; 2. 单值二叉树&#x1f332; 2.1 思路分析&#x1f52e; 2.2 代码实现&#x1f488; 3. 二叉树的前序遍历&#x1f39f;️ 3.1 思路分…

鲜花植物企业网站建设的效果如何

从整体来看&#xff0c;似乎鲜花植物只会在线下花市或花店里购买或见到&#xff0c;但其实对鲜花植物批发商或品牌商来说&#xff0c;线上渠道同样重要&#xff0c;从本地来说&#xff0c;流量少且获取难&#xff0c;各家品牌/门店竞争激烈&#xff0c;并且无法全面展示自己的品…

基于模型驱动的可解释性全色、多光谱、高光谱融合网络

摘要 摘要:同时融合高光谱(HS)、多光谱(MS)和全色(PAN)图像为生成高分辨率HS (HRHS)图像提供了一种新的范式。在这项研究中&#xff0c;我们提出了一个可解释的模型驱动的深度网络&#xff0c;用于HS, MS和PAN图像融合&#xff0c;称为HMPNet。我们首先提出了一种新的融合模型…

计算机组成原理第6章-(计算机的运算方法)【上】

机器数与真值 把符号“数字化”的数称为机器数,而把带“+”、“-”符号的数称为真值。 原码表示法 原码是机器数中最简单的一种表示形式,0表示整数,1表示负数。 约定整数的符号位和数值位之间用“逗号”隔开。 在原码中,0有两种表示形式:“+0”和“-0”是不一样的。 反…

毅速:3D打印随形水路已经逐步向压铸模具普及

随着科技的不断发展&#xff0c;3D打印技术已经逐渐渗透到各个领域。其中&#xff0c;3D打印随形水路在注塑模具中已经广泛应用&#xff0c;目前正逐渐向压铸模具普及。 传统CNC等减材工艺的水路制造&#xff0c;可以在模具中生产出平直的冷却水路&#xff0c;但这种工艺难以加…

Qt/C++视频监控Onvif工具/组播搜索/显示监控画面/图片参数调节/OSD管理/祖传原创

一、前言 能够写出简单易用而又不失功能强大的组件&#xff0c;一直是我的追求&#xff0c;简单主要体现在易用性&#xff0c;不能搞一些繁琐的流程和一些极难使用的API接口&#xff0c;或者一些看不懂的很难以理解的函数名称&#xff0c;一定是要越简单越好。功能强大主要体现…

百度网盘win11端扫一扫登录二维码无法显示问题的解决方法

百度网盘win11端扫一扫登录二维码无法显示问题的解决方法 文章目录 问题描述解决方法1、打开Edge 设置2、进入Reset settings页面3、单击重置按钮&#xff1a;4、重新打开百度网盘APP 问题描述 今天在win11端扫描登录百度云网盘时&#xff0c;二维码无法加载出来&#xff0c;具…

PCIe surprise down异常与DPC功能分析-part2

DPC是PCIe协议中的一项功能&#xff0c;旨在防止由于一个设备的错误而影响到整个系统。当一个PCIe设备检测到严重的、不可恢复的错误时&#xff0c;它可能会触发DPC过程。在这个过程中&#xff0c;PCIe开关会隔离受影响的下游端口&#xff0c;阻止任何进一步的数据传输和请求通…

中北大学 软件构造 U+及上课代码详解

作业1 1.数据类型可分为两类:(原子类型) 、结构类型。 2.(数据结构)是计算机存储、组织数据的方式&#xff0c;是指相互之间存在一种或多种特定关系的数据元素的集合 3.代码重构指的是改变程序的(结构)而不改变其行为&#xff0c;以便提高代码的可读性、易修改性等。 4.软件实…

语法分析树(先看例子再看定义)

语法分析树&#xff08;先看例子再看定义&#xff09; 先讲例子 书上讲问题&#xff0c;先讲定义&#xff0c;一顿学术操作&#xff0c;让人云里雾里&#xff0c;然后出例子。其实这样往往让人觉得看书的过程就是放弃的过程。 关于语法分析树&#xff0c;我先从上篇文章的例…

二叉树进阶题目(超详解)

文章目录 前言根据二叉树创建字符串题目分析写代码 二叉树的层序遍历题目分析 写代码二叉树的层序遍历II题目分析写代码 二叉树的最近公共祖先题目分析写代码时间复杂度 优化思路优化的代码 二叉搜索树与双向链表题目分析写代码 从前序与中序遍历序列构造二叉树题目分析写代码从…

线性回归简介

线性回归简介 1、情景描述2、线性回归 1、情景描述 假设&#xff0c;我们现在有这么一张图&#xff1a; 其中&#xff0c;横坐标x表示房子的面积&#xff0c;纵坐标y表示房价。我们猜想x与y之间存在线性关系&#xff1a; y k x b ykxb ykxb 现在&#xff0c;思考一个问题&…

PHP HTTPoxy CGI 应用程序漏洞 CVE-2016-5385

HTTPoxy CGI 应用程序漏洞 CVE-2016-5385 已亲自复现 漏洞名称漏洞描述影响版本 漏洞复现环境搭建漏洞利用 修复建议 漏洞名称 漏洞描述 在Oracle Communications BRM 10.x/12.x&#xff08;云软件&#xff09;中发现漏洞。它已经被宣布为关键。此漏洞影响组件用户数据库的未…

RabbitMQ如何做到不丢不重

目录 MQTT协议 如何保证消息100%不丢失 生产端可靠性投递 ​编辑 RabbitMQ的Broker端投 &#xff08;1&#xff09;消息持久化 &#xff08;2&#xff09;设置集群镜像模式 &#xff08;3&#xff09;消息补偿机制 消费端 ACK机制改为手动 总结 MQTT协议 先来说下MQTT…