0

0

java中lambda表达式语法说明

高洛峰

高洛峰

发布时间:2017-01-23 15:42:10

|

2046人浏览过

|

来源于php中文网

原创

语法说明

一个lambda表达式由如下几个部分组成:

1. 在圆括号中以逗号分隔的形参列表。在CheckPerson.test方法中包含一个参数p,代表了一个Person类的实例。注意:lambda表达式中的参数的类型是可以省略的;此外,如果只有一个参数的话连括号也是可以省略的。比如上一节曾提到的代码:

p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
    && p.getAge() <= 25

2. 箭头符号:->。用来分隔参数和函数体。

3. 函数体。由一个表达式或代码块组成。在上一节的例子中使用了这样的表达式:

立即学习Java免费学习笔记(深入)”;

   
p.getGender() == Person.Sex.MALE
      && p.getAge() >= 18
      && p.getAge() <= 25

如果使用的是表达式,java运行时会计算并返回表达式的值。另外,还可以选择在代码块中使用return语句:

p -> {
  return p.getGender() == Person.Sex.MALE
      && p.getAge() >= 18
      && p.getAge() <= 25;
}

不过return语句并不是表达式。在lambda表达式中需要将语句用花括号括起来,然而却没有必要在只是调用一个返回值为空的方法时也用花括号括起来,所以如下的写法也是正确的:

email -> System.out.println(email)

lambda表达式和方法的声明看起来有很多类似的地方。所以也可以把lambda表达式视为匿名方法,也就是没有定义名字的方法。

以上提到的lambda表达式都是只使用了一个参数作为形参的表达式。下面的实例类,Caulator,演示了如何使用多个参数作为形参:

package com.zhyea.zytools;
 
public class Calculator {
 
  interface IntegerMath {
    int operation(int a, int b);
  }
 
  public int operateBinary(int a, int b, IntegerMath op) {
    return op.operation(a, b);
  }
 
  public static void main(String... args) {
    Calculator myApp = new Calculator();
    IntegerMath addition = (a, b) -> a + b;
    IntegerMath subtraction = (a, b) -> a - b;
    System.out.println("40 + 2 = " + myApp.operateBinary(40, 2, addition));
    System.out.println("20 - 10 = " + myApp.operateBinary(20, 10, subtraction));
  }
}

代码中operateBinary方法使用了两个整型参数执行算数操作。这里的算数操作本身就是IntegerMath接口的一个实例。在上面的程序中使用lambda表达式定义了两个算数操作:addition和subtraction。执行程序会打印如下内容:

40 + 2 = 42
20 - 10 = 10

访问外部类的局部变量 

类似于局部类或匿名类,lambda表达式也可以访问外部类的局部变量。不同的是,使用lambda表达式时无需考虑覆盖之类的问题。lambda表达式只是一个词法上的概念,这意味着它不需要从超类中继承任何名称,也不会引入新的作用域。也就是说,在lambda表达式中的声明和在它的外部环境中的声明意义是一样的。在下面的例子中对此作了演示:

package com.zhyea.zytools;
 
import java.util.function.Consumer;
 
public class LambdaScopeTest {
 
  public int x = 0;
 
  class FirstLevel {
 
    public int x = 1;
 
    void methodInFirstLevel(int x) {
      //如下的语句会导致编译器在statement A处报错“local variables referenced from a lambda expression must be final or effectively final”
      // x = 99;
      Consumer<integer> myConsumer = (y) ->{
        System.out.println("x = " + x); // Statement A
        System.out.println("y = " + y);
        System.out.println("this.x = " + this.x);
        System.out.println("LambdaScopeTest.this.x = " + LambdaScopeTest.this.x);
      };
 
      myConsumer.accept(x);
    }
  }
 
  public static void main(String... args) {
    LambdaScopeTest st = new LambdaScopeTest();
    LambdaScopeTest.FirstLevel fl = st.new FirstLevel();
    fl.methodInFirstLevel(23);
  }
}

   

这段代码会输出如下内容:

   
x = 23
y = 23
this.x = 1
LambdaScopeTest.this.x = 0

如果使用示例中lambda表达式myConsumer中的参数y替换为x,编译器就会报错:

Consumer<integer> myConsumer = (x) ->{
      // ....
    };

编译器报错信息是:“variable x is already defined in method methodInFirstLevel(int)”,就是说在方法methodInFirstLevel中已经定义了变量x。报错是因为lambda表达式不会引入新的作用域。也因此呢,可以在lambda表达式中直接访问外部类的域字段、方法以及形参。在这个例子中,lambda表达式myConsumer直接访问了方法methodInFirstLevel的形参x。而访问外部类的成员时也是直接使用this关键字。在这个例子中this.x指的就是FirstLevel.x。

然而,和局部类或匿名类一样,lambda表达式也只能访问局部变量或外部被声明为final(或等同于final)的成员。比如,我们将示例代码methodInFirstLevel方法中“x=99”前面的注释去掉:

//如下的语句会导致编译器在statement A处报错“local variables referenced from a lambda expression must be final or effectively final”
x = 99;
Consumer<integer> myConsumer = (y) ->{
  System.out.println("x = " + x); // Statement A
  System.out.println("y = " + y);
  System.out.println("this.x = " + this.x);
  System.out.println("LambdaScopeTest.this.x = " + LambdaScopeTest.this.x);
};

因为在这段语句中修改了参数x的值,使得methodInFirstLevel的参数x不可以再被视为final式的。因此java编译器就会在lambda表达式访问局部变量x的地方报出类似“local variables referenced from a lambda expression must be final or effectively final”这样的错误。

目标类型

该如何判断lambda表达式的类型呢。再来看一下筛选适龄服兵役人员的代码:

   
p -> p.getGender() == Person.Sex.MALE
       && p.getAge() >= 18
       && p.getAge() <= 25

这段代码在两处用到过:

Venice
Venice

Venice AI是一个专注于隐私保护的去中心化生成式AI平台,可以生成无审查和无偏见的AI对话和图像

下载

public static void printPersons(List roster, CheckPerson tester) —— 方案三 
public void printPersonsWithPredicate(List roster, Predicate tester) —— 方案六 

在调用printPersons方法时,这个方法期望一个CheckPerson 类型的参数,此时上面的那个表达式就是一个CheckPerson 类型的表达式。在调用printPersonsWithPredicate方法时,期望一个Predicate类型的参数,此时同样的表达式就是Predicate类型的。像这样子的,由方法期望的类型来决定的类型就叫做目标类型(其实我觉得scala中的类型推断用在这里更合适)。java编译器就是通过目标类型的上下文语境或者发现lambda表达式时的位置来判断lambda表达式的类型的。这也就意味着只能在Java编译器可以推断出类型的位置使用Lambda表达式:

变量声明;

赋值;

返回语句;

数组初始化;

方法或者构造器参数;

lambda表达式方法体;

条件表达式(?:);

抛出异常时。 

目标类型和方法参数

对于方法参数,Java编译器还需要依赖两个语言特性来决定目标类型:重载解析和类型参数推断。

看一下下面的这两个函数式接口( java.lang.Runnable and java.util.concurrent.Callable):

public interface Runnable {
    void run();
  }
 
  public interface Callable<v> {
    V call();
  }

Runnable.run()方法没有返回值,而Callable.call()方法有。

假设我们像下面这样重载了invoke方法:

void invoke(Runnable r) {
   r.run();
 }
 
 <t> T invoke(Callable<t> c) {
   return c.call();
 }

那么在下面的语句中将会调用哪个方法呢:

String s = invoke(() -> "done");

调用的是invoke(Callable),因为这个方法有返回值,而invoke(Runnable)没有返回值。在这种情况下lambda表达式(() -> “done”)的类型是Callable

序列化

如果一个lambda表达式的目标类型还有它调用的参数的类型都是可序列化的,那么lambda表达式也是可序列化的。然而就像内部类一样,强烈不建议对lambda表达式进行序列化。

更多java中lambda表达式语法说明相关文章请关注PHP中文网!

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法

本专题系统整理pixiv网页版官网入口及登录访问方式,涵盖官网登录页面直达路径、在线阅读入口及快速进入方法说明,帮助用户高效找到pixiv官方网站,实现便捷、安全的网页端浏览与账号登录体验。

928

2026.02.13

微博网页版主页入口与登录指南_官方网页端快速访问方法
微博网页版主页入口与登录指南_官方网页端快速访问方法

本专题系统整理微博网页版官方入口及网页端登录方式,涵盖首页直达地址、账号登录流程与常见访问问题说明,帮助用户快速找到微博官网主页,实现便捷、安全的网页端登录与内容浏览体验。

307

2026.02.13

Flutter跨平台开发与状态管理实战
Flutter跨平台开发与状态管理实战

本专题围绕Flutter框架展开,系统讲解跨平台UI构建原理与状态管理方案。内容涵盖Widget生命周期、路由管理、Provider与Bloc状态管理模式、网络请求封装及性能优化技巧。通过实战项目演示,帮助开发者构建流畅、可维护的跨平台移动应用。

183

2026.02.13

TypeScript工程化开发与Vite构建优化实践
TypeScript工程化开发与Vite构建优化实践

本专题面向前端开发者,深入讲解 TypeScript 类型系统与大型项目结构设计方法,并结合 Vite 构建工具优化前端工程化流程。内容包括模块化设计、类型声明管理、代码分割、热更新原理以及构建性能调优。通过完整项目示例,帮助开发者提升代码可维护性与开发效率。

29

2026.02.13

Redis高可用架构与分布式缓存实战
Redis高可用架构与分布式缓存实战

本专题围绕 Redis 在高并发系统中的应用展开,系统讲解主从复制、哨兵机制、Cluster 集群模式及数据分片原理。内容涵盖缓存穿透与雪崩解决方案、分布式锁实现、热点数据优化及持久化策略。通过真实业务场景演示,帮助开发者构建高可用、可扩展的分布式缓存系统。

103

2026.02.13

c语言 数据类型
c语言 数据类型

本专题整合了c语言数据类型相关内容,阅读专题下面的文章了解更多详细内容。

54

2026.02.12

雨课堂网页版登录入口与使用指南_官方在线教学平台访问方法
雨课堂网页版登录入口与使用指南_官方在线教学平台访问方法

本专题系统整理雨课堂网页版官方入口及在线登录方式,涵盖账号登录流程、官方直连入口及平台访问方法说明,帮助师生用户快速进入雨课堂在线教学平台,实现便捷、高效的课程学习与教学管理体验。

17

2026.02.12

豆包AI网页版入口与智能创作指南_官方在线写作与图片生成使用方法
豆包AI网页版入口与智能创作指南_官方在线写作与图片生成使用方法

本专题汇总豆包AI官方网页版入口及在线使用方式,涵盖智能写作工具、图片生成体验入口和官网登录方法,帮助用户快速直达豆包AI平台,高效完成文本创作与AI生图任务,实现便捷智能创作体验。

764

2026.02.12

PostgreSQL性能优化与索引调优实战
PostgreSQL性能优化与索引调优实战

本专题面向后端开发与数据库工程师,深入讲解 PostgreSQL 查询优化原理与索引机制。内容包括执行计划分析、常见索引类型对比、慢查询优化策略、事务隔离级别以及高并发场景下的性能调优技巧。通过实战案例解析,帮助开发者提升数据库响应速度与系统稳定性。

92

2026.02.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
AngularJS教程
AngularJS教程

共24课时 | 3.8万人学习

【李炎恢】ThinkPHP8.x 后端框架课程
【李炎恢】ThinkPHP8.x 后端框架课程

共50课时 | 4.7万人学习

php初学者入门课程
php初学者入门课程

共10课时 | 0.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号