Lambda表达式

匿名类的一个问题是,如果匿名类的实现非常简单,例如只包含一个方法的接口,那么匿名类的语法可能看起来不实用且不清楚。在这些情况下,您通常会尝试将功能作为参数传递给另一个方法,例如当有人单击按钮时应采取的操作。Lambda表达式使您可以执行此操作,将功能视为方法参数,或将代码视为数据。

一页•  小道•  下一页»

Java教程是为JDK 8编写的。本页描述的示例和实践没有利用后续版本中引入的改进。

Lambda表达式

匿名类的一个问题是,如果匿名类的实现非常简单,例如只包含一个方法的接口,那么匿名类的语法可能看起来不实用且不清楚。在这些情况下,您通常会尝试将功能作为参数传递给另一个方法,例如当有人单击按钮时应采取的操作。Lambda表达式使您可以执行此操作,将功能视为方法参数,或将代码视为数据。

上一节“ 匿名类 ”向您展示了如何在不给它命名的情况下实现基类。虽然这通常比命名类更简洁,但对于只有一个方法的类,即使是匿名类也似乎有点过分和繁琐。Lambda表达式允许您更紧凑地表达单方法类的实例。

Lambda表达式的理想用例

假设您正在创建社交网络应用程序。您希望创建一项功能,使管理员能够对满足特定条件的社交网络应用程序成员执行任何类型的操作,例如发送消息。下表详细描述了此用例:

领域 描述
名称 对选定的成员执行操作
主要演员 管理员
前提条件 管理员已登录系统。
后置条件 仅对符合指定条件的成员执行操作。
主要成功案例
  1. 管理员指定要执行特定操作的成员的条件。
  2. 管理员指定要对这些选定成员执行的操作。
  3. 管理员选择“ 提交”按钮。
  4. 系统将查找与指定条件匹配的所有成员。
  5. 系统对所有匹配成员执行指定的操作。
扩展

1A。管理员可以选择在指定要执行的操作之前或选择“ 提交”按钮之前预览符合指定条件的成员。

发生频率 白天很多次。

假设此社交网络应用程序的成员由以下Person类表示 :

公共类人{public enum Sex {男,女}字符串名称;LocalDate生日;性别;字符串emailAddress;public int getAge(){// ...}public void printPerson(){// ...}
}

假设您的社交网络应用程序的成员存储在一个List<Person>实例中。方法我介绍一种,其他的可以查看其官网。

创建搜索匹配一个特征的成员的方法

一种简单的方法是创建几种方法; 每种方法都会搜索与一个特征匹配的成员,例如性别或年龄。以下方法打印超过指定年龄的成员:

public static void printPersonsOlderThan(List <Person> roster,int age){for(Person p:roster){if(p.getAge()> = age){p.printPerson();}}
}

注意:A List是有序的 Collection。甲集合是一个对象,该组中的多个元素到单个单元中。集合用于存储,检索,操作和传递聚合数据。有关集合的更多信息,请参阅 集合跟踪。

这种方法可能会使您的应用程序变得脆弱,这是由于引入了更新(例如更新的数据类型)导致应用程序无法工作的可能性。假设您升级应用程序并更改Person类的结构,使其包含不同的成员变量; 也许该类记录和测量年龄与不同的数据类型或算法。您必须重写大量API以适应此更改。此外,这种方法是不必要的限制; 例如,如果您想要打印年龄小于某个年龄的成员,该怎么办?

Lambda表达式的语法

lambda表达式包含以下内容:

  • 括号中用逗号分隔的形式参数列表。该CheckPerson.test方法包含一个参数, p表示Person该类的实例 。

    注意:您可以省略lambda表达式中参数的数据类型。此外,如果只有一个参数,则可以省略括号。例如,以下lambda表达式也是有效的:

    p  - > p.getGender()== Person.Sex.MALE && p.getAge()> = 18&& p.getAge()<= 25
  • 箭头标记, ->

  • 一个主体,由单个表达式或语句块组成。此示例使用以下表达式:

    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表达式中,必须用braces({})括起语句。但是,您不必在大括号中包含void方法调用。例如,以下是有效的lambda表达式:

    电子邮件 - > System.out.println(电子邮件)

请注意,lambda表达式看起来很像方法声明; 您可以将lambda表达式视为匿名方法 - 没有名称的方法。

以下示例 Calculator是一个lambda表达式的示例,它采用多个形式参数:

公共类计算器{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){计算器myApp = new Calculator();IntegerMath add =(a,b) - > a + b;IntegerMath减法=(a,b) - > a  -  b;System.out.println(“40 + 2 =”+myApp.operateBinary(40,2,另外));System.out.println(“20  -  10 =”+myApp.operateBinary(20,10,减法));    }
}

该方法operateBinary对两个整数操作数执行数学运算。操作本身由实例指定IntegerMath。的例子中定义了lambda表达式两个操作,additionsubtraction。该示例打印以下内容:

40 + 2 = 42
20  -  10 = 10

访问封闭范围的局部变量

像本地和匿名类一样,lambda表达式可以 捕获变量 ; 它们对封闭范围的局部变量具有相同的访问权限。但是,与本地和匿名类不同,lambda表达式没有任何阴影问题(有关更多信息,请参阅 阴影)。Lambda表达式是词法范围的。这意味着它们不会从超类型继承任何名称或引入新级别的范围。lambda表达式中的声明与封闭环境中的声明一样被解释。以下示例 LambdaScopeTest演示了这一点:

import java.util.function.Consumer;公共类LambdaScopeTest {public int x = 0;class FirstLevel {public int x = 1;void methodInFirstLevel(int x){//以下语句导致编译器生成//错误“从lambda表达式引用的局部变量//必须是最终的或有效的最终“在声明A中://// x = 99;消费者<整数> myConsumer =(y) - > {System.out.println(“x =”+ x); //声明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表达式的声明中替换参数x代替,则编译器会生成错误:ymyConsumer

消费者<整数> myConsumer =(x) - > {// ...
}

编译器生成错误“变量x已在方法methodInFirstLevel(int)中定义”,因为lambda表达式不会引入新的作用域级别。因此,您可以直接访问封闭范围的字段,方法和局部变量。例如,lambda表达式直接访问x方法的参数methodInFirstLevel。要访问封闭类中的变量,请使用关键字this。在此示例中,this.x引用成员变量FirstLevel.x

但是,与本地和匿名类一样,lambda表达式只能访问最终或有效最终的封闭块的局部变量和参数。例如,假设您在methodInFirstLevel定义语句后立即添加以下赋值语句:

void methodInFirstLevel(int x){x = 99;// ...
}

由于这个赋值语句,变量FirstLevel.x不再是有效的最终结果。因此,Java编译器生成类似于“从lambda表达式引用的局部变量必须是final或者final final”的错误消息,其中lambda表达式myConsumer尝试访问FirstLevel.x变量:

System.out.println(“x =”+ x);

目标打字

你如何确定lambda表达式的类型?回想一下lambda表达式,它选择了男性和年龄在18到25岁之间的成员:

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

这个lambda表达式用于以下两种方法:

  • public static void printPersons(List<Person> roster, CheckPerson tester)在方法3:在局部类指定搜索条件码

  • public void printPersonsWithPredicate(List<Person> roster, Predicate<Person> tester)在方法6:Lambda表达式使用标准的功能接口

当Java运行时调用该方法时printPersons,它期望数据类型为CheckPerson,因此lambda表达式属于此类型。但是,当Java运行时调用该方法时printPersonsWithPredicate,它期望数据类型为Predicate<Person>,因此lambda表达式属于此类型。这些方法所期望的数据类型称为目标类型。要确定lambda表达式的类型,Java编译器将使用发现lambda表达式的上下文或情境的目标类型。因此,您只能在Java编译器可以确定目标类型的情况下使用lambda表达式:

  • 变量声明

  • 分配

  • 退货声明

  • 数组初始化器

  • 方法或构造函数参数

  • Lambda表达体

  • 条件表达式, ?:

  • 转换表达式

目标类型和方法参数

对于方法参数,Java编译器使用另外两种语言特性确定目标类型:重载解析和类型参数推断。

考虑以下两个功能接口( java.lang.Runnable和 java.util.concurrent.Callable<V>):

public interface Runnable {void run();
}公共接口Callable <V> {V call();
}

该方法Runnable.run不返回值,而是返回值Callable<V>.call

假设您已invoke按如下方式重载方法(有关重载方法的详细信息,请参阅 定义方法):

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

将在以下语句中调用哪个方法?

String s = invoke(() - >“done”);

该方法invoke(Callable<T>)将被调用因为该方法返回的值; 方法 invoke(Runnable)没有。在这种情况下,lambda表达式的类型() -> "done"Callable<T>

序列化

如果lambda表达式的目标类型及其捕获的参数是可序列化的,则可以 序列化它。但是,与 内部类一样,强烈建议不要对lambda表达式进行序列化。

各种方法参考

有四种方法参考:

参考静态方法 ContainingClass::staticMethodName
引用特定对象的实例方法 containingObject::instanceMethodName
引用特定类型的任意对象的实例方法 ContainingType::methodName
引用构造函数 ClassName::new

参考静态方法

方法引用Person::compareByAge是对静态方法的引用。

引用特定对象的实例方法

以下是对特定对象的实例方法的引用示例:

class ComparisonProvider {public int compareByName(Person a,Person b){return a.getName()。compareTo(b.getName());}public int compareByAge(Person a,Person b){return a.getBirthday()。compareTo(b.getBirthday());}
}
ComparisonProvider myComparisonProvider = new ComparisonProvider();
Arrays.sort(rosterAsArray,myComparisonProvider :: compareByName);

方法引用myComparisonProvider::compareByName调用compareByName作为对象一部分的方法myComparisonProvider。JRE推断出方法类型参数,在本例中是(Person, Person)

对特定类型的任意对象的实例方法的引用

以下是对特定类型的任意对象的实例方法的引用示例:

String [] stringArray = {“芭芭拉”,“詹姆斯”,“玛丽”,“约翰”,“Patricia”,“Robert”,“Michael”,“Linda”};
Arrays.sort(stringArray,String :: compareToIgnoreCase);

方法引用的等效lambda表达式String::compareToIgnoreCase将具有形式参数列表(String a, String b),其中ab是用于更好地描述此示例的任意名称。方法引用将调用该方法a.compareToIgnoreCase(b)

参考构造函数

您可以使用名称以与静态方法相同的方式引用构造函数new。以下方法将元素从一个集合复制到另一个集合:

public static <T,SOURCE扩展Collection <T>,DEST扩展Collection <T >>DEST transferElements(SOURCE sourceCollection,供应商<DEST> collectionFactory){DEST result = collectionFactory.get();for(T t:sourceCollection){result.add(T);}返回结果;
}

功能接口Supplier包含一个get不带参数并返回对象的方法。因此,您可以transferElements使用lambda表达式调用该方法,如下所示:

设置<Person> rosterSetLambda =transferElements(roster,() - > {return new HashSet <>();});

您可以使用构造函数引用代替lambda表达式,如下所示:

设置<Person> rosterSet = transferElements(roster,HashSet :: new);

Java编译器推断您要创建HashSet包含类型元素的集合Person。或者,您可以按如下方式指定:

设置<Person> rosterSet = transferElements(名册,HashSet <Person> :: new);

转载于:https://www.cnblogs.com/xasdh/p/10762717.html

Java lambda expression相关推荐

  1. java lambda函数_Java SE 8新功能介绍:使用Lambda Expression进行函数式编程

    java lambda函数 " Java SE 8新功能浏览 "系列的这篇文章将深入了解Lambda表达式 . 我将向您展示Lambda表达式的几种不同用法. 它们都具有功能接口的 ...

  2. Java SE 8新特性导览:使用Lambda Expression进行函数式编程

    " Java SE 8新功能浏览 "系列的这篇文章将深入了解Lambda表达式 . 我将向您展示Lambda表达式的几种不同用法. 它们都具有功能接口的共同实现. 我将解释编译器如 ...

  3. java expression 用法_浅析Java 8新特性Lambda Expression

    什么是Lambda Expression 对于Lambda Expression,我的理解是,它是一个函数表达式,如下: (int x, int y) -> x - y 符号左边定义了函数的输入 ...

  4. java lambda表达式_高性能的 Lambda 表达式,简洁优雅图文并茂

    来源:知乎 Mingqi 链接:https://www.zhihu.com/question/20125256/answer/324121308 有网友问,Lambda 表达式有何用处?如何使用?在P ...

  5. Java Lambda表达式入门

    本文转自:http://blog.csdn.net/renfufei... 转载请注明出处 原文链接: Start Using Java Lambda Expressions 下载示例程序 Examp ...

  6. java拉姆达表达式事例,Java Lambda表达式详解和实例

    简介 Lambda表达式是Java SE 8中一个重要的新特性.lambda表达式允许你通过表达式来代替功能接口. lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体( ...

  7. Java Lambda表达式初探

    前言 本文受启发于Trisha Gee在JavaOne 2016的主题演讲Refactoring to Java 8. Java 8已经发行两年多,但很多人仍然在使用JDK7.对企业来说,技术上谨慎未 ...

  8. java lambda 实现_Java 8 Lambda实现原理分析

    PDF文档已上传Github 为了支持函数式编程,Java 8引入了Lambda表达式,那么在Java 8中到底是如何实现Lambda表达式的呢? Lambda表达式经过编译之后,到底会生成什么东西呢 ...

  9. Java Lambda表达

    Java 8 lambda表达式示例 我个人对Java 8发布非常激动,尤其是lambda表达式和流API.越来越多的了解它们,我能写出更干净的代码.虽然一开始并不是这样.第一次看到用lambda表达 ...

最新文章

  1. network-manager
  2. biomaRt包下载转录本信息
  3. ajax更新,AJAX网址更新(AJAX URL update)
  4. 简单了解request与response
  5. 转:delphi用URLDownloadToFile下载文件,用进度条跟踪下载进度
  6. 2018-2019-1 20165318 20165322 20165326 实验一 开发环境的熟悉
  7. python爬取基金历史净值_Python爬取天天基金网历史净值数据
  8. 算法设计与分析——动态规划——最长公共子序列
  9. GoLang语言多版本管理工具--GVM入门介绍
  10. python和台达plc通讯_台达PLC通信协议ModbusASCIIDVP
  11. ipad 开发常用问题
  12. Undelete Plus 2.53
  13. 渔村小厂,如何成长为5G霸王
  14. Mac上的windows 10系统,bootcamp 5更新 bootcamp 6,缺少bootcamp.msi
  15. Android开发之殇
  16. [转载] 七龙珠第一部——第111话 龟仙人最后的魔封波
  17. RNN、self-attention、transform的浅显或许错误的理解
  18. python海龟教程_Python 零基础 快速入门 趣味教程 (咪博士 海龟绘图 turtle) 7. 条件循环...
  19. 一招解决bat文件执行时cmd命令窗口闪退问题
  20. MySQL主外键设置

热门文章

  1. Mac上的一位数密码你知道吗
  2. macos server 恢复安装_如何通过 macOS 恢复功能重新安装 macOS
  3. PHP批量查询数据库下载远程文件脚本实例
  4. java 读取1m文件_java的FileInputStream类读取文件
  5. dedecms调用某一顶级栏目下二级标签
  6. PHP笔记-所有错误统一输出404页面(详细错误日志输出,提高安全性)
  7. HTTP笔记-SOAP基本概念
  8. Linux笔记-centos7替换yum及编译安装mydumper
  9. C++工作笔记-虚函数、纯虚函数、虚析构函数的进一步理解
  10. Java基础入门笔记-关系操作符