java8中的lambda表达式简介
作者:morris131 发布时间:2022-09-12 04:14:10
Lambda表达式类似匿名函数,简单地说,它是没有声明的方法,也即没有访问修饰符、返回值声明和方法名。
Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
Lambda表达式的语法
(parameters) -> expression
或
(parameters) -> { statements; }
参数说明:
可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指明表达式返回了一个数值。
举例说明:
// 1. 不需要参数,返回值为5
() -> 5
// 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)
Lambda表达式作用域
lambda表达式中可以引用任何外部的变量或者常量。但是对这些外部的变量是有要求的:它们必须是Effectively final的。
局部内部类和匿名内部类访问的局部变量必须由final修饰,java8开始,可以不加final修饰符,由系统默认添加。java将这个功能称为:Effectively final功能。
方法引用
指向静态方法的方法引用
Function<String, Integer> function1 = Integer::parseInt; // 等价于下面
Function<String, Integer> function2 = (String i) -> Integer.parseInt(i);
指向任意类型实例方法的方法引用
Function<String, String> function3 = String::toLowerCase; // 等价于下面
Function<String, String> function4 = (String i) -> i.toLowerCase();
BiFunction<String, Integer, String> biFunction = (String s, Integer i) -> s.substring(i);
BiFunction<String, Integer, String> biFunction2 = String::substring;
指向现有对象的实例方法的方法引用
String str = "hello";
Supplier<Integer> supplier = () -> str.length();
Supplier<Integer> supplier2 = str::length;
Function<Integer, String> function5 = (Integer i) -> str.substring(i);
Function<Integer, String> function6 = str::substring;
构造方法引用
package com.morris.java8.lamdba;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
public class MethodReferenceExample {
public static void main(String[] args) {
// 构造函数引用
Supplier<String> stringSupplier = () -> new String();
Supplier<String> stringSupplier2 = String::new;
Function<String, String> stringFunction = (String s)->new String(s);
Function<String, String> stringFunction2 = String::new;
BiFunction<Runnable, String, Thread> stringBiFunction = (Runnable r, String b)-> new Thread(r, b);
BiFunction<Runnable, String, Thread> stringBiFunction2 = Thread::new;
ThreeFunction<ThreadGroup, Runnable, String, Thread> threeFunction = (ThreadGroup g, Runnable r, String b)-> new Thread(g, r, b);
ThreeFunction<ThreadGroup, Runnable, String, Thread> threeFunction2 = Thread::new;
}
interface ThreeFunction<A, B, C, D> {
D triple(A a, B b, C c);
}
}
lambda与匿名内部类
从表面上看到Lambda表达式似乎只是为了简化匿名内部类书写,这看起来仅仅通过语法糖在编译阶段把所有的Lambda表达式替换成匿名内部类就可以了。但实际并非如此。在JVM层面,Lambda表达式和匿名内部类有着明显的差别。
匿名内部类
匿名内部类仍然是一个类,只是不需要程序员显示指定类名,编译器会自动为该类取名。
public class AnonymousClassDemo {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("this is an Anonymous class demo");
}
});
}
}
因此上面的代码,编译之后将会产生两个class文件:
AnonymousClassDemo.class
AnonymousClassDemo$1.class
进一步分析主类AnonymousClassDemo.class的字节码,可发现其创建了匿名内部类的对象:
$ javap -v -p AnonymousClassDemo.class
...
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=1, args_size=1
0: new #2 // class java/lang/Thread
3: dup
4: new #3 // class AnonymousClassDemo$1 创建匿名内部类
7: dup
8: invokespecial #4 // Method AnonymousClassDemo$1."<init>":()V
11: invokespecial #5 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
14: pop
15: return
LineNumberTable:
line 5: 0
line 11: 15
}
SourceFile: "AnonymousClassDemo.java"
InnerClasses:
static #3; //class AnonymousClassDemo$1
lambda表达式
Lambda表达式通过invokedynamic指令实现,不会产生新的类。
public class LambdaDemo {
public static void main(String[] args) {
new Thread(()-> System.out.println("this is a lambda demo"));
}
}
上面的代码编译之后只有一个class文件:
LambdaDemo.class
通过javap查看LambdaDemo.class的字节码,我们更能看出Lambda表达式内部表示的不同。
$ javap -v -p LambdaDemo.class
...
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=1, args_size=1
0: new #2 // class java/lang/Thread
3: dup
4: invokedynamic #3, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable; 使用invokedynamic指令调用
9: invokespecial #4 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
12: pop
13: return
LineNumberTable:
line 4: 0
line 5: 13
private static void lambda$main$0(); // Lambda表达式被封装成主类的私有方法
descriptor: ()V
flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=2, locals=0, args_size=0
0: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #6 // String this is a lambda demo
5: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 4: 0
}
SourceFile: "LambdaDemo.java"
InnerClasses:
public static final #51= #50 of #54; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
BootstrapMethods:
0: #22 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#23 ()V
#24 invokestatic LambdaDemo.lambda$main$0:()V
#23 ()V
反编译之后我们发现Lambda表达式被封装成了主类的一个私有方法,并通过invokedynamic指令进行调用。
既然Lambda表达式不是内部类的简写,那么Lambda内部的this引用也就跟内部类对象没什么关系了。在Lambda表达式中this的意义跟在表达式外部完全一样。
匿名内部类可以为任意接口创建实例——不管接口包含多少个抽象方法,只要匿名内部类实现所有的抽象方法即可。但Lambda表达式只能为函数式接口创建实例。
匿名内部类可以为抽象类、甚至普通类创建实例,但Lambda表达式只能为函数式接口创建实例。
匿名内部类实现的抽象方法的方法体允许调用接口中定义的默认方法;但Lambda表达式的代码块不允许调用接口中定义的默认方法。
来源:https://blog.csdn.net/u022812849/article/details/125463711


猜你喜欢
- RecyclerView 滑动时的优化处理,在滑动时停止加载图片,在滑动停止时开始加载图片,这里用了Glide.pause 和Glide.r
- 需求描述现在有这样一个需求:我有A、B两台服务器,其中A是一个视频处理服务器,B是一个数据存储服务器。此时有一个视频需要先在A服务器上进行一
- 我们平时使用的一些常见队列都是非阻塞队列,比如PriorityQueue、LinkedList(LinkedList是双向链表,它实现了De
- 一、服务端TcpListener server = new TcpListener(IPAddress.Parse("127.0.
- 一,Java Object Serialization1,什么是序列化(Serialization)序列化是指将结构化对象转化为字节流以便在
- 文档更新说明2018年09月24日 v1.0 初稿代理在生活中很常见,比如说婚介网站,其实就是找对象的代理;还有社保代理、人事代理;还有找黄
- 1.内部类概念及分类将一个类定义在另一个类的内部或者接口内部或者方法体内部,这个类就被称为内部类,我们不妨将内部类所在的类称为外围类,除了定
- 1.UUID 简介UUID 含义是通用唯一识别码 (Universally Unique Identifier),这是一个软件建构的标准。也
- 前言最近看了内部类后,总结一下,首先内部类嵌套在其他内部的类,根据出现的位置和关键字,可以分为以下四种类:成员内部类,静态内部类,方法内部类
- 本文实例为大家分享了Java实现简易计算器的具体代码,供大家参考,具体内容如下程序的运行环境为Windows10 ,编译环境为IDEA。计算
- 准备工具:IDEAjdk1.8Navicat for MySQLPostman一、新建Project选择依赖:mybatis Web Mys
- 引言最近在写一个 Mybatis 代码自动生成插件,用的是Mybatis来扩展,其中有一个需求就是 生成javaMapper文件和 xmlM
- 背景Android开发中,加载图片过多、过大很容易引起OutOfMemoryError异常,即我们常见的内存溢出。因为Android对单个应
- 本文实例讲述了C#使用GDI+创建缩略图的方法,分享给大家供大家参考。具体方法分析如下:C#的Gdi+还是相当好用的。创建缩略图步骤如下:1
- 目录特性引入依赖使用特性Kotlin + Flow 实现的 Android 应用初始化任务启动库。支持模块化,按模块加载任务可指定工作进程名
- Java实现并发的几种方法Java程序默认以单线程方式运行。synchronizedJava 用过synchronized 关键字来保证一次
- 一、环境说明集群环境至少需要3个节点(也就是3台服务器设备):1个Master,2个Slave,节点之间局域网连接,可以相互ping通,下面
- 原理 Redis Cluster 一般由多个节点组成,节点数量至少为 6 个才能保证组成完整高可用的集群,其中三个为主
- 有httponly的cookie,在httpwebreqeust请求时,会获取不到,可以采用直接获取head中的set-cookie,再转换
- 在我们应用程序的业务逻辑中,经常会碰到参数校验的情况,手动的在代码层上面进行校验就会带来很不好的体验,阅读、维护的成本会大大增加,造成冗余。