全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

Java中Lambda表达式之Lambda语法与作用域解析

接上一篇:初探Lambda表达式/Java多核编程【2】并行与组合行为

本节是第二章开篇,前一章已经浅显地将所有新概念点到,书中剩下的部分将对这些概念做一个基础知识的补充与深入探讨实践。

本章将介绍Lambda表达式基础知识。

前言

把上一张书中的结语放到这里作为本章学习内容的开头,以此来概括Lambda表达式的优点:

  • 提升性能、自动的并行化
  • 更棒的API(comparing(...)细粒度的方法将成为标准)
  • 编码风格得到改进、代码简化

反观前面几篇文章中的代码实践,以上三个优点全部得到了验证。

Lambda语法

前文中我们已经提到,Java中无法声明独立的纯函数,但是Lambda的出现提供了一种与独立函数更为近似的实现方式。就只看Lambda形式,的确与很多精简语法的脚本语言中所声明的函数高度相似:

# CoffeeScript
eat = (x) -> 
 alert("#{x} has been eatten!")

总之光看上去就像那么回事:)

那么Lambda表达式的语法又是怎样的呢?

  • 参数列表
  • Lambda体

两部分之间使用->分割,看几个例子:

p -> p.translate();
i -> new Point();
(a, b) -> return a + b;
() -> "Ha!";
(x, y, z) -> {
 x += y;
 y += z;
 z += x;
}

箭头左边接收任意数量的参数,右边则为表达式体,描述所需的行为。

显而易见,在一般情况下无需显式地指定参数类型,除非上下文的信息无法是编译器推断出相应的类型:

(int x, int y) -> x + y;

参数可以声明为final,也可以添加注解(@Nullable, etc.)。

表达式体部分可以为方法的调用,如str.length()等等,也可以是表达式,如加减乘除等等,即“语句Lambda”与“表达式Lambda”这两种形式。

另外关于返回值,有则用return sth_to_return;,没有则用return;或直接不写返回语句。

最后,需要注意的是Lambda表达式不需要也不允许使用throws关键字来声明可能产生并需要向上抛出的异常。

Lambda与匿名内部类

前几篇文章中常常将Lambda与匿名内部类做粗浅的类比与对比,现在我们将就这一点做具体深入的分析。

语法

首先在语法层面,Lambda表达式有时候被称为匿名内部类的“语法糖”,这表明了二者之间存在语法繁简的明显区别。

无标识性问题

其次便是标识性问题,我们知道Java中为了区分对象,每一个对象(即使是匿名内部类的实例)都具有唯一标识,而依赖于对象而存在的行为(即我们所说的方法)也会与此标识相关联。
例如:

String bar = "bar";
String foo = "foo";
System.out.println(bar.hashCode()); // => 97299
System.out.println(foo.hashCode()); // => 101574

但是对于Lambda表达式而言,情况便不是如此的明朗,根据具体情况的不同,Lambda自身可能拥有标识也可能没有。

况且,Lambda为的就是表示一种行为,趋向于纯函数,因此一般情况下是不需要使用标识加以区分的。

作用域规则

再者就是两者的作用域大小的区别。

对于匿名内部类而言,显而易见,在类内可以沿用父类型(即函数接口)的名字。

而对于Lambda,则不能。

我们用Runnable接口来举一个例子:

public interface LetsRun extends Runnable {
 String aString = "Big brother is watching.";
}
new Thread(
 new LetsRun() {
  @Override
  public void run() {
   System.out.println(aString);
  }
}).run();

显然,匿名内部类能够直接沿用我们在LetsRun这个函数式接口中声明的aString。

写完这段代码的同时,IDE给了我一个可以将匿名内部类折叠为Lambda的提示,现在就让它帮我们自动折叠一下:

new Thread((LetsRun) () -> System.out.println(LetsRun.aString)).run();

注意此时需要打印的内容也同时自动变成了LetsRun.aString,印证了上述特征,即Lambda不能直接访问父类型中的名字。

关于对外部变量的访问(后面书中将此称为“变量捕获”),不论是匿名内部类还是Lambda,对于域外部变量的权限都是有限的。

在匿名内部类中,可以读取外部量,但是不允许有修改变量的倾向。

也就是说,没有严格的限制规定被访问的外部量必须被声明为final:

// This is OK
String anotherString = "WAR IS PEACE / FREEDOM IS SLAVERY / IGNORANCE IS STRENGTH";
// Also OK
final String finalString = "Nineteen Eighty-Four";
new Thread(new LetsRun() {
 @Override
 public void run() {
  System.out.println(aString + "\n" + anotherString + "\n" + finalString);
 }
}).run();

倘若一旦在方法内修改了anotherString的值,编译就无法通过。
同样,折叠为Lambda后,依然是合法的:

new Thread((LetsRun) ()
  -> System.out.println(LetsRun.aString + "\n" + anotherString + "\n" + finalString)).run();

关于变量捕获的问题是下一小节的重点内容,在此暂时不做深究。

Lambda表达式在定义时,参数部分与表达式体内的命名可以暂时屏蔽掉字段的名称:

public class Foo {
 String x, y;
 BinaryOperator binaryOperator = (x, y) -> x.hashCode() + y.hashCode();
 // ...
}

另外,Lambda相当于语句块,因此表达式体内持有和外部相同的语境,即this与super拥有相同含义:

public class MySuperClass {
 public static final String aString = "Father";
}
public class MyClass extends MySuperClass {
 public static final String aString = "Son";
 public void aMethod() {
  new Thread((LetsRun) () -> {
   System.out.println("--- Lambda ---");
   System.out.println(super.aString);
   System.out.println(this.aString);
  }).run();
  System.out.println("--- Outside ---");
  System.out.println(super.aString);
  System.out.println(this.aString);
 }
}

运行结果:

--- Lambda ---
Father
Son
--- Outside ---
Father
Son

Lambda无法引用自身,因此可以用一种尴尬的方式递归调用自己:

intUnaryOperator = i -> i == 0 ? 1 : i * intUnaryOperator.applyAsInt(i - 1);

小结

Lambda不从父类型中继承任何名字,包括:

接口的静态final字段

接口的静态嵌套类

默认方法(将在后续介绍)

将全部被排除在作用域之外。

Lambda参数与表达式体中的局部声明可以屏蔽字段名。

Lambda中的this和super的含义完全同外部一致。

而若在匿名内部类访问外部对象的当前实例须用OuterClass.this,非常笨拙:

new Thread((LetsRun) () ->
  System.out.println(Foo.this.getClass().toString())
).run();

递归Lambda时须注意Lambda变量无法被初始化,只能直接调用相应函数式接口中的方法。

本章代码:

Foo.java

import java.util.function.BinaryOperator;
public class Foo {
 String x, y;
 BinaryOperator binaryOperator = (x, y) -> x.hashCode() + y.hashCode();
 public static void main(String[] args) {
  String bar = "bar";
  String foo = "foo";
  System.out.println(bar.hashCode());
  System.out.println(foo.hashCode());
  new Thread((LetsRun) () -> System.out.println(LetsRun.aString)).run();
  String anotherString = "WAR IS PEACE / FREEDOM IS SLAVERY / IGNORANCE IS STRENGTH";
  final String finalString = "Nineteen Eighty-Four";
  new Thread((LetsRun) () -> System.out.println(LetsRun.aString + "\n" + anotherString + "\n" + finalString)).run();
  new MyClass().aMethod();
  new Foo().accessOuterClassInAnnoymousInnerClass();
 }
 public void accessOuterClassInAnnoymousInnerClass() {
  new Thread((LetsRun) () ->
    System.out.println(Foo.this.getClass().toString())
  ).run();
 }
}

LetsRun.java

public interface LetsRun extends Runnable {
 String aString = "Big brother is watching.";
}

MyClass.java

import java.util.function.IntUnaryOperator;
public class MyClass extends MySuperClass {
 public static final String aString = "Son";
 IntUnaryOperator intUnaryOperator = null;
 public void aMethod() {
  new Thread((LetsRun) () -> {
   System.out.println("--- Lambda ---");
   System.out.println(super.aString);
   System.out.println(this.aString);
  }).run();
  System.out.println("--- Outside ---");
  System.out.println(super.aString);
  System.out.println(this.aString);
 }
 public void factorial() {
  intUnaryOperator = i -> i == 0 ? 1 : i * intUnaryOperator.applyAsInt(i - 1);
 }
}

MySuperClass.java

public class MySuperClass {
 public static final String aString = "Father";
}

以及运行结果:

97299
101574
Big brother is watching.
Big brother is watching.
WAR IS PEACE / FREEDOM IS SLAVERY / IGNORANCE IS STRENGTH
Nineteen Eighty-Four
--- Lambda ---
Father
Son
--- Outside ---
Father
Son
class Foo

以上所述是小编给大家介绍的Java中Lambda表达式之Lambda语法与作用域解析,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!


# java  # lambda语法  # lambda表达式语法  # Java编程中使用lambda表达式的奇技淫巧  # java使用lambda表达式对List集合进行操作技巧(JDK1.8)  # Java8新特性lambda表达式有什么用(用法实例)  # Java8 用Lambda表达式给List集合排序的实现  # Java8新特性Lambda表达式的一些复杂用法总结  # Java8中lambda表达式的应用及一些泛型相关知识  # java lambda表达式用法总结  # Java8中Lambda表达式使用和Stream API详解  # 使用Java 8 Lambda表达式将实体映射到DTO的操作  # java中lambda表达式的分析与具体用法  # 递归  # 在此  # 不需要  # 多核  # 书中  # 显而易见  # 小编  # 则用  # 性问题  # 几篇  # 的是  # 体内  # 都是  # 几个  # 也不  # 加减乘除  # 也会  # 就像  # 又是  # 将在 


相关文章: 长沙企业网站制作哪家好,长沙水业集团官方网站?  建站之星24小时客服电话如何获取?  建站之星ASP如何实现CMS高效搭建与安全管理?  如何用搬瓦工VPS快速搭建个人网站?  零基础网站服务器架设实战:轻量应用与域名解析配置指南  建站之星微信建站一键生成小程序+多端营销系统  网站建设制作需要多少钱费用,自己做一个网站要多少钱,模板一般多少钱?  如何在万网主机上快速搭建网站?  php8.4新语法match怎么用_php8.4match表达式替代switch【方法】  如何通过二级域名建站提升品牌影响力?  如何选择香港主机高效搭建外贸独立站?  ,巨量百应是干嘛的?  制作证书网站有哪些,全国城建培训中心证书查询官网?  如何使用Golang安装API文档生成工具_快速生成接口文档  微信网站制作公司有哪些,民生银行办理公司开户怎么在微信网页上查询进度?  如何彻底卸载建站之星软件?  如何在阿里云域名上完成建站全流程?  如何用低价快速搭建高质量网站?  企业网站制作费用多少,企业网站空间一般需要多大,费用是多少?  如何在腾讯云服务器上快速搭建个人网站?  高端企业智能建站程序:SEO优化与响应式模板定制开发  如何在万网自助建站平台快速创建网站?  如何选择长沙网站建站模板?H5响应式与品牌定制哪个更优?  油猴 教程,油猴搜脚本为什么会网页无法显示?  香港服务器如何优化才能显著提升网站加载速度?  巅云智能建站系统:可视化拖拽+多端适配+免费模板一键生成  湖北网站制作公司有哪些,湖北清能集团官网?  网站制作与设计教程,如何制作一个企业网站,建设网站的基本步骤有哪些?  百度网页制作网站有哪些,谁能告诉我百度网站是怎么联系?  利用JavaScript实现拖拽改变元素大小  如何快速登录WAP自助建站平台?  微网站制作教程,不会写代码,不会编程,怎么样建自己的网站?  建站之星后台管理如何实现高效配置?  电商网站制作多少钱一个,电子商务公司的网站制作费用计入什么科目?  公司网站的制作公司,企业网站制作基本流程有哪些?  制作网站外包平台,自动化接单网站有哪些?  红河网站制作公司,红河事业单位身份证如何上传?  建站之星2.7模板:企业网站建设与h5定制设计专题  如何在IIS7上新建站点并设置安全权限?  国美网站制作流程,国美电器蒸汽鍋怎么用官方网站?  广平建站公司哪家专业可靠?如何选择?  北京的网站制作公司有哪些,哪个视频网站最好?  建站之星官网登录失败?如何快速解决?  h5在线制作网站电脑版下载,h5网页制作软件?  如何快速启动建站代理加盟业务?  建站之星安装提示数据库无法连接如何解决?  网站制作培训多少钱一个月,网站优化seo培训课程有哪些?  详解免费开源的DotNet二维码操作组件ThoughtWorks.QRCode(.NET组件介绍之四)  ,想在网上投简历,哪几个网站比较好?  Swift中swift中的switch 语句 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。