Java中使用Lambda表达式和函数编程示例
目录
- 1、简单介绍
- 2、Lambdas和Scopes
- 3、Lambdas与局部变量
- 4、Lambda体与局部变量
- 5、Lambdas和'This'和'Super'关键字
- 6、Lambdas和Exceptions
- 7、预定义的功能接口
1、简单介绍
第一个示例演示变量声明上下文中的
lambda
。它将lambda()->{System.out.println(“running”);}
分配给可运行接口类型的变量r。第二个示例类似,但演示了赋值上下文中的
lambda
(到先前声明的变量r)。第三个示例演示了
return
语句上下文中的lambda。它使用指定的文件扩展名参数调用getFilter()方法以返回java.io.FileFilter对象。该对象被传递给java.io.File的listFiles()
方法,该方法为每个文件调用过滤器,忽略与扩展名不匹配的文件。getFilter()
方法返回通过lambda
表示的FileFilter对象。编译器注意到lambda
满足此函数接口的boolean accept
(文件路径名)方法(两者都有一个参数,lambda主体返回一个布尔值),并将lambda绑定到FileFilter。第四个示例演示了lambda在数组初始值设定项上下文中的用法。基于lambdas创建了两个
java.nio.file.PathMatcher
对象。每个PathMatcher
对象根据其lambda
主体指定的条件匹配文件。以下是相关代码:final PathMatcher matchers[] ={(path) -> path.toString().endsWith("txt"),(path) -> path.toString().endsWith("java")};
PathMatcher
函数接口提供一个boolean matches(Path path)
方法,该方法与lambda的参数列表及其主体的布尔返回类型一致。随后调用此方法以确定在访问当前目录和子目录期间遇到的每个文件的匹配项(基于文件扩展名)。第五个示例演示线程构造函数上下文中的
lambda
。第六个示例演示了
lambda
上下文中的lambda
,这表明lambda
可以嵌套。第七个示例演示了三元条件表达式(?:)上下文中的
lambda
:根据升序或降序排序从两个lambda中选择一个。第八个(也是最后一个)示例演示了强制转换表达式上下文中的
lambda。()->System.getProperty(“user.name”)lambda
被强制转换为PrivilegedAction
函数接口类型。此强制转换解决了java.security.AccessController
类中的歧义,该类声明了以下方法:staticT doPrivileged(PrivilegedAction action)static T doPrivileged(PrivilegedExceptionAction action)
问题是
PrivilegedAction
和PrivilegedExceptionAction
的每个接口都声明了相同的T run()方法。由于编译器无法确定哪个接口是目标类型,因此在没有强制转换的情况下会报告错误。编译清单4并运行应用程序。您应该观察以下输出,该输出假定
LambdaDemo.java
是当前目录中唯一的.java文件,并且该目录不包含.txt文件:runningrunningFound matched file: '.\LambdaDemo.java'.runningcalledWashingtonSydneyRomeOttawaMoscowLondonJerusalemBerlinjeffrey
2、Lambdas和Scopes 术语范围是指程序中名称与特定实体(例如变量)绑定的部分。在程序的另一部分中,名称可能绑定到另一个实体。lambda主体不会引入新的作用域。相反,它的作用域是封闭作用域。
3、Lambdas与局部变量
lambda
主体可以定义局部变量。因为这些变量被认为是封闭范围的一部分,所以编译器在检测到lambda主体正在重新定义局部变量时将报告错误。清单5演示了这个问题。清单5。LambdaDemo.java(版本5)
public class LambdaDemo{public static void main(String[] args){int limit = 10; Runnable r = () -> {int limit = 5; for (int i = 0; i < limit; i++)System.out.println(i); }; }}
因为
limit
已经存在于封闭范围(main()
方法)中,lambda
主体对limit的重新定义(int limit=5;)
会导致编译器报告以下错误消息:错误:变量limit已经在方法main(字符串[])中定义。4、Lambda体与局部变量 无论是源于
lambda
主体还是在封闭范围内,局部变量在使用之前都必须初始化。否则,编译器将报告错误。在lambda主体外部定义并从主体引用的局部变量或参数必须标记为
final
或视为有效final
(初始化后无法将该变量指定给)。试图修改一个有效的最终变量会导致编译器报告一个错误,如清单6所示。【Java中使用Lambda表达式和函数编程示例】清单6。LambdaDemo.java(版本6)
public class LambdaDemo{public static void main(String[] args){int limit = 10; Runnable r = () -> {limit = 5; for (int i = 0; i < limit; i++)System.out.println(i); }; }}
限制实际上是最终的。lambda主体试图修改此变量会导致编译器报告错误。这样做是因为final/final变量需要挂起,直到lambda执行为止,这可能要在定义变量的代码返回后很久才会发生。非最终/非有效最终变量不再存在。
5、Lambdas和'This'和'Super'关键字 lambda主体中使用的任何
this
或super
引用都被视为等同于其在封闭范围中的用法(因为lambda不引入新范围)。然而,匿名类的情况并非如此,如清单7所示。清单7。LambdaDemo.java(版本7)
public class LambdaDemo{public static void main(String[] args){new LambdaDemo().doWork(); }public void doWork(){System.out.printf("this = %s%n", this); Runnable r = new Runnable(){@Overridepublic void run(){System.out.printf("this = %s%n", this); }}; new Thread(r).start(); new Thread(() -> System.out.printf("this = %s%n", this)).start(); }}
清单7的
main()
方法实例化LambdaDemo
并调用对象的doWork()
方法来输出对象的this引用,实例化一个实现Runnable的匿名类,创建一个线程对象,在其线程启动时执行此Runnable
,并创建另一个线程对象,其线程在启动时执行lambda
。编译清单7并运行应用程序。您应该观察与以下输出类似的情况:
this = LambdaDemo@776ec8dfthis = LambdaDemo$1@48766bbthis = LambdaDemo@776ec8df
第一行显示
LambdaDemo
的this引用,第二行显示新可运行范围中不同的this引用,第三行显示lambda上下文中的this引用。第三行和第一行匹配,因为lambda
的作用域嵌套在doWork()方法中;这在整个方法中具有相同的含义。6、Lambdas和Exceptions
lambda
主体不允许抛出比函数接口方法的throws子句中指定的更多的异常。如果lambda
主体抛出异常,则函数接口方法的throws
子句必须声明相同的异常类型或其超类型。考虑清单8。清单8。LambdaDemo.java(版本8)
import java.awt.AWTException; import java.io.IOException; @FunctionalInterfaceinterface Work{void doSomething() throws IOException; }public class LambdaDemo{public static void main(String[] args) throws AWTException, IOException{Work work = () -> { throw new IOException(); }; work.doSomething(); work = () -> { throw new AWTException(""); }; }}
清单8声明了一个工作函数接口,其
doSomething()
方法声明为抛出java.io.IOException。main()
方法将抛出IOException
的lambda
分配给work,这是正常的,因为IOException
列在doSomething()
的throws子句中。main()
接下来分配一个lambda,该lambda抛出java.awt.AWTException
来工作。但是,编译器不允许此赋值,因为AWTException
不是doSomething()
的throws子句的一部分(当然也不是IOException
的子类型)。7、预定义的功能接口 您可能会发现自己反复创建类似的功能接口。例如,您可以使用布尔
IsConnection
(连接c)方法创建CheckConnection
函数接口,使用布尔isPositiveBalance
(帐户帐户)方法创建CheckAccount
函数接口。这是浪费。前面的示例公开了谓词(布尔值函数)的抽象概念。Oracle提供了常用功能接口的
java.util.function包
,以预测这些模式。例如,这个包的PredicateCheckConnection
和CheckAccount
。Predicate
提供一个boolean test(T t)
方法,该方法根据其argument (t)计算该谓词,当T与predicate
匹配时返回true,否则返回false。请注意,test()提供了与isConnected()
和isPositiveBalance()
相同的参数列表。另外,请注意,它们都具有相同的返回类型(布尔值)。清单9中的应用程序源代码演示了谓词
清单9。LambdaDemo.java(版本9)
import java.util.ArrayList; import java.util.List; import java.util.function.Predicate; class Account{private int id, balance; Account(int id, int balance){this.balance = balance; this.id = id; }int getBalance(){return balance; }int getID(){return id; }void print(){System.out.printf("Account: [%d], Balance: [%d]%n", id, balance); }}public class LambdaDemo{static List accounts; public static void main(String[] args){accounts = new ArrayList<>(); accounts.add(new Account(1000, 200)); accounts.add(new Account(2000, -500)); accounts.add(new Account(3000, 0)); accounts.add(new Account(4000, -80)); accounts.add(new Account(5000, 1000)); // Print all accountsprintAccounts(account -> true); System.out.println(); // Print all accounts with negative balances.printAccounts(account -> account.getBalance() < 0); System.out.println(); // Print all accounts whose id is greater than 2000 and less than 5000.printAccounts(account -> account.getID() > 2000 &&account.getID() < 5000); }static void printAccounts(Predicate tester){for (Account account: accounts)if (tester.test(account))account.print(); }}
清单9创建了一个基于数组的帐户列表,其中有正余额、零余额和负余额。然后,它通过使用
lambdas
调用printAccounts()
来演示谓词考虑lambda表达式帐户->真。编译器验证
lambda
是否匹配谓词编译清单9并运行应用程序。我们能观察以下输出:
Account: [1000], Balance: [200]Account: [2000], Balance: [-500]Account: [3000], Balance: [0]Account: [4000], Balance: [-80]Account: [5000], Balance: [1000]Account: [2000], Balance: [-500]Account: [4000], Balance: [-80]Account: [3000], Balance: [0]Account: [4000], Balance: [-80]
Predicate
只是java.util.function
的各种预定义函数接口之一。另一个示例是Consumer,
它表示接受单个参数但不返回结果的操作。与Predicate
不同,Consumer
预期通过副作用进行操作。换句话说,它以某种方式修改了它的论点。使用者的
void accept(T)
方法对其argument(T)
执行操作。当出现在此函数接口的上下文中时,lambda
必须符合accept()
方法的单独参数和返回类型。到此这篇关于Java中使用Lambda表达式和函数编程示例的文章就介绍到这了,更多相关Java中使用
Lambda
表达式和函数编程内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!推荐阅读
- 热闹中的孤独
- JAVA(抽象类与接口的区别&重载与重写&内存泄漏)
- Shell-Bash变量与运算符
- JS中的各种宽高度定义及其应用
- 2021-02-17|2021-02-17 小儿按摩膻中穴-舒缓咳嗽
- 深入理解Go之generate
- 由浅入深理解AOP
- 异地恋中,逐渐适应一个人到底意味着什么()
- 【译】20个更有效地使用谷歌搜索的技巧
- 我眼中的佛系经纪人