Java新特性:Lambda表达式

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() {@Overridepublic 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";@Overridepublic 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();// 结果:hellostudent.noParameterNoReturn.action();// 结果:Studentstudent.noParameterNoReturn2.action();}
}

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

public class Student {// 外部类的属性String name = "Student";int age;NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn() {// 内部类自己的属性String name = "hello";@Overridepublic 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 遍历 listlist.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>() {@Overridepublic 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>() {@Overridepublic 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.hqwc.cn/news/19462.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Django_haystack全文搜索

haystack是全文搜索的框架&#xff0c;支持whoosh、solr、Xapian、Elasticsearc四种全文检索引擎&#xff0c;点击查看官方网站。 whoosh是用纯Python编写的全文搜索引擎&#xff0c;虽然性能比不上sphinx、xapian、Elasticsearc等&#xff0c;但是无二进制包&#xff0c;程序…

一百二十八、Kettle——从Hive增量导入到ClickHouse

一、目标 用Kettle把Hive的DWS层数据增量导入到ClickHouse中 工具版本&#xff1a;Kettle&#xff1a;8.2 Hive:3.1.2 ClickHouse21.9.5.16 全量导入请访问拙作链接 http://t.csdn.cn/Rqvuvhttp://t.csdn.cn/Rqvuv 二、前提准备 &#xff08;一&#xff09;kettl…

Vue3 +Echarts5 可视化大屏——屏幕适配

项目基于Vue3 Echarts5 开发&#xff0c;屏幕适配是使用 scale 方案 Echarts组件按需引入&#xff0c;减少打包体积 地图组件封装&#xff08;全国&省份地图按需加载&#xff09; 效果图&#xff1a; 屏幕适配 大屏适配常用的方案有 rem vw/vh 和 scale 。 rem vw/vh …

vue 使用 npm run dev命令后 自动打开浏览器为谷歌

文章目录 需求分析 需求 vue 启动后&#xff0c;想要其自动打开指定浏览器&#xff08;谷歌&#xff09;并设置要打开的IP地址和端口号 分析 package.json 打开package.json文件加上 --open chrome index.js 打开index.js文件&#xff0c;将浏览器设置为自动打开

模板类与继承

模板类与继承 模板类继承普通类普通类继承模板类的实例化版本。普通类继承模板类模板类继承模板类模板类继承模板参数给出的基类 模板类继承普通类 基类 派生类 测试函数; 普通类继承模板类的实例化版本。 模板基类 普通类继承模板基类的实例化版本&#xff1a; 普通…

LVS和keepa lived群集

keepa lived 简述 一.keepalived 服务重要功能 1管理LS负载均衡器软件 keepalived可以通过读取自身的配置文件&#xff0c;实现通过更底层的接口直接管理Lvs配置以及服务的启动 停止功能 这会使 LVS应用跟更加简便 2 支持故障自动切换 (failover) ①两台知己同时安装好kee…

Kotlin~Decorator装饰器模式

概念 装饰模式指的是在不必改变原类文件和使用继承的情况下&#xff0c;动态地扩展一个对象的功能。它是通过创建一个包装对象&#xff0c;也就是装饰来包裹真实的对象。可以运行时动态添加新的功能&#xff0c;而无需改变原来的代码。 特点&#xff1a; 灵活扩展新的功能动态…

buu-Reverse-[2019红帽杯]childRE

目录 [2019红帽杯]childRE 修饰函数名和函数签名是什么&#xff1f; 对于变换部分的具体分析&#xff1a; [2019红帽杯]childRE 下载附件&#xff0c;查壳&#xff0c;无壳 在IDA中打开&#xff0c;定位主函数 int __cdecl main(int argc, const char **argv, const char …

Kotlin~责任链模式

概念 允许多个对象按顺序处理请求或任务。 角色介绍 Handler: 处理器接口,提供设置后继者&#xff08;可选&#xff09;ConcreteHandler&#xff1a;具体处理器&#xff0c;处理请求 UML 代码实现 比如ATM机吐钱就可以使用责任链实现。 class PartialFunction<in P1, o…

POLARDB IMCI 白皮书 云原生HTAP 数据库系统 一 列式数据是如何存储与处理的

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到2群&#xff08;共…

无聊猿惨遭亏本甩卖 “贵族”持有人沦为“大冤种

被称为NFT贵族的无聊猿&#xff08;BAYC&#xff09;正在进行挥泪大甩卖&#xff0c;歌手黄立成等持有者们近期不断抛售BAYC NFT&#xff0c;导致该系列NFT地板价一度跌至30 ETH&#xff08;约5.7万美元&#xff09;以下。 一年多以前&#xff0c;BAYC还是时尚界的宠儿&#x…

JavaCV实现byte[]转RTMP流

需求&#xff1a;通过私有的api我可以不断收到byte[]形式的视频数据&#xff0c;现在我需要处理这些数据&#xff0c;最终推送出RTMP流。 实现&#xff1a;通过管道流将不断收到的byte[]视频数据转化为输入流然后提供给JavaCV的FFmpegFrameGrabber使用&#xff0c;然后通过FFmp…