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<String>函数接口类型。此强制转换解决了java.security.AccessController类中的歧义,该类声明了以下方法:

static <T> T doPrivileged(PrivilegedAction<T> action)
static <T> T doPrivileged(PrivilegedExceptionAction<T> action)

问题是PrivilegedActionPrivilegedExceptionAction的每个接口都声明了相同的T run()方法。由于编译器无法确定哪个接口是目标类型,因此在没有强制转换的情况下会报告错误。

编译清单4并运行应用程序。您应该观察以下输出,该输出假定LambdaDemo.java是当前目录中唯一的.java文件,并且该目录不包含.txt文件:

running
running
Found matched file: '.\LambdaDemo.java'.
running
called
Washington
Sydney
Rome
Ottawa
Moscow
London
Jerusalem
Berlin
jeffrey

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所示。

清单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主体中使用的任何thissuper引用都被视为等同于其在封闭范围中的用法(因为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()
                       {
                          @Override
                          public 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@776ec8df
this = LambdaDemo$1@48766bb
this = 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;
@FunctionalInterface
interface 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()方法将抛出IOExceptionlambda分配给work,这是正常的,因为IOException列在doSomething()的throws子句中。

main()接下来分配一个lambda,该lambda抛出java.awt.AWTException来工作。但是,编译器不允许此赋值,因为AWTException不是doSomething()的throws子句的一部分(当然也不是IOException的子类型)。

7、预定义的功能接口

您可能会发现自己反复创建类似的功能接口。例如,您可以使用布尔IsConnection(连接c)方法创建CheckConnection函数接口,使用布尔isPositiveBalance(帐户帐户)方法创建CheckAccount函数接口。这是浪费。

前面的示例公开了谓词(布尔值函数)的抽象概念。Oracle提供了常用功能接口的java.util.function包,以预测这些模式。例如,这个包的Predicate<T>功能接口可以用来代替CheckConnectionCheckAccount

Predicate<T>提供一个boolean test(T t)方法,该方法根据其argument (t)计算该谓词,当T与predicate匹配时返回true,否则返回false。请注意,test()提供了与isConnected()isPositiveBalance()相同的参数列表。另外,请注意,它们都具有相同的返回类型(布尔值)。

清单9中的应用程序源代码演示了谓词<T>。

清单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<Account> 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 accounts
      printAccounts(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<Account> tester)
   {
      for (Account account: accounts)
         if (tester.test(account))
            account.print();
   }
}

清单9创建了一个基于数组的帐户列表,其中有正余额、零余额和负余额。然后,它通过使用lambdas调用printAccounts()来演示谓词<T>,以便打印出所有帐户,仅打印出那些余额为负数的帐户,以及仅打印出ID大于2000且小于5000的帐户。

考虑lambda表达式帐户->真。编译器验证lambda是否匹配谓词<T>的布尔测试(T)方法,它会这样做——lambda提供单个参数(account),其主体始终返回布尔值(true)。对于这个lambda,test()被实现为执行return true;。

编译清单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<T>只是java.util.function的各种预定义函数接口之一。另一个示例是Consumer<T>,它表示接受单个参数但不返回结果的操作。与Predicate<T>不同,Consumer<T>预期通过副作用进行操作。换句话说,它以某种方式修改了它的论点。

使用者的void accept(T)方法对其argument(T)执行操作。当出现在此函数接口的上下文中时,lambda必须符合accept()方法的单独参数和返回类型。

到此这篇关于Java中使用Lambda表达式和函数编程示例的文章就介绍到这了,更多相关Java中使用Lambda表达式和函数编程内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 吊打Java面试官之Lambda表达式 Stream API

    目录 一.jdk8新特性简介 二.Lambda表达式 简单理解一下Lambda表达式 Lambda表达式的使用 三.函数式接口 1.什么是函数式接口 2.如何理解函数式接口 3.Java内置四大核心函数式接口 四.方法引用与构造器引用 方法引用 构造器引用和数组引用 五.Stream API 1.Stream API的说明 2.为什么要使用Stream API 3.创建Stream的四种方式 4.Stream的中间操作及其测试 5.Stream的终止操作及其测试 六.Optional类的使用 O

  • 详解Java函数式编程和lambda表达式

    为什么要使用函数式编程 函数式编程更多时候是一种编程的思维方式,是种方法论.函数式与命令式编程的区别主要在于:函数式编程是告诉代码你要做什么,而命令式编程则是告诉代码要怎么做.说白了,函数式编程是基于某种语法或调用API去进行编程.例如,我们现在需要从一组数字中,找出最小的那个数字,若使用用命令式编程实现这个需求的话,那么所编写的代码如下: public static void main(String[] args) { int[] nums = new int[]{1, 2, 3, 4, 5,

  • Java函数式编程(一):你好,Lambda表达式

    第一章 你好,lambda表达式! 第一节 Java的编码风格正面临着翻天覆地的变化. 我们每天的工作将会变成更简单方便,更富表现力.Java这种新的编程方式早在数十年前就已经出现在别的编程语言里面了.这些新特性引入Java后,我们可以写出更简洁,优雅,表达性更强,错误更少的代码.我们可以用更少的代码来实现各种策略和设计模式. 在本书中我们将通过日常编程中的一些例子来探索函数式风格的编程.在使用这种全新的优雅的方式进行设计编码之前,我们先来看下它到底好在哪里. 改变了你的思考方式 命令式风格--

  • Java语法中Lambda表达式无法抛出异常的解决

    目录 Lambda表达式无法抛出异常 1.Demo 例子 2.编译通过 lambda表达式异常应该如何处理 我们看一个例子 Lambda表达式无法抛出异常 1.Demo 例子 错误提示 - Unhandled exception: java.io.IOException; public static void main(String[] args) throws IOException{ Stream.of("a", "b", "c").forE

  • 详解Java8中的lambda表达式、::符号和Optional类

    Java8中的lambda表达式.::符号和Optional类 0. 函数式编程 函数式编程(Functional Programming)属于编程范式(Programming Paradigm)中的用语,此外还有命令式编程(Imperative Programing)等,有兴趣的同学可以自行了解,我们这里大概解释一下函数式编程,在函数式编程中,输入一旦确定了,输出都确定了,函数调用的结果只依赖于传入的输入变量和内部逻辑,不依赖于外部,这样的写出的函数没有副作用.举个例子: public cla

  • 一文带你掌握Java8中Lambda表达式 函数式接口及方法构造器数组的引用

    目录 函数式接口概述 函数式接口示例 1.Runnable接口 2.自定义函数式接口 3.作为参数传递 Lambda 表达式 内置函数式接口 Lambda简述 Lambda语法 方法引用 构造器引用 数组引用 函数式接口概述 只包含一个抽象方法的接口,称为函数式接口. 可以通过 Lambda 表达式来创建该接口的对象. 可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口.同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口.

  • Java中Lambda表达式的使用详解

    目录 理解函数式接口以及 Lambda表达式的发展过程 Lambda表达式及语法 一起来看看具体的使用 你需要注意什么 Lambda的实际运用 1.对集合排序 2.遍历集合 3.遍历集合(带条件) 4.代替 Runnable,开启一个线程 理解函数式接口以及 Lambda表达式的发展过程 任何接口,只包含唯一一个抽象方法,就是函数式接口 /** * lambdab表达式的发展 */ public class TestLambda1 { //3.静态内部类 static class Like2 i

  • 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

  • 关于Java 中的 Lambda 表达式

    这篇文章我们将讨论关于Java 中的 Lambda 表达式,Lambda 表达式是 Java 涉足函数式编程的过程.它接受参数并将其应用于表达式或代码块.以下是语法的基本示例: (parameter1, parameter2) => expression 或者 (parameter1, parameter2) => {code block} Lambda 表达式非常有限,如果它不是 void,则必须立即返回一个值.他们不能使用诸如 if 或 for 之类的关键字来保持简单性.如果需要更多行代码

  • 详解Java中的Lambda表达式

    简介 Lambda表达式是Java SE 8中一个重要的新特性.lambda表达式允许你通过表达式来代替功能接口. lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块). Lambda表达式还增强了集合库. Java SE 8添加了2个对集合数据进行批量操作的包: java.util.function 包以及java.util.stream 包. 流(stream)就如同迭代器(iterator),但附加了许多额外的功能.

  • 深入理解Java中的Lambda表达式

    Java 8 开始出现,带来一个全新特性:使用 Lambda 表达式 (JSR-335) 进行函数式编程.今天我们要讨论的是 Lambda 的其中一部分:虚拟扩展方法,也叫做公共辩护(defender)方法.该特性可以让你在接口定义中提供方法的默认实现.例如你可以为已有的接口(如 List 和 Map)声明一个方法定义,这样其他开发者就无需重新实现这些方法,有点像抽象类,但实际却是接口.当然,Java 8 理论上还是兼容已有的库. 虚拟扩展方法为 Java 带来了多重继承的特性,尽管该团队声称与

  • 快速入门Java中的Lambda表达式

    Lambda简介 Lambda表达式是Java SE 8中一个重要的新特性.lambda表达式允许你通过表达式来代替功能接口. lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块). Lambda表达式还增强了集合库. Java SE 8添加了2个对集合数据进行批量操作的包: java.util.function 包以及 java.util.stream 包. 流(stream)就如同迭代器(iterator),但附加了许多

  • Java 8 Lambda 表达式比较器使用示例代码

    引言 在这个例子中,我们将向您展示如何使用 java8 lambda 表达式编写一个 Comparator 来对 List 进行排序. 经典的比较器示例: Comparator<Developer> byName = new Comparator<Developer>() { @Override public int compare(Developer o1, Developer o2) { return o1.getName().compareTo(o2.getName());

  • Java8与Scala中的Lambda表达式深入讲解

    前言 最近几年Lambda表达式风靡于编程界.很多现代编程语言都把它作为函数式编程的基本组成部分.基于JVM的编程语言如Scala.Groovy及Clojure把它作为关键部分集成在语言中.而如今,(最终)Java 8也加入了这个有趣的行列. Java8 终于要支持Lambda表达式!自2009年以来Lambda表达式已经在Lambda项目中被支持.在那时候,Lambda表达式仍被称为Java闭包.在我们进入一些代码示例以前,先来解释下为什么Lambda表达式在Java程序员中广受欢迎. 1.为

  • Lambda表达式原理及示例

    Lambda表达式   Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性. Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中). 使用 Lambda 表达式可以使代码变的更加简洁紧凑. 1. 需求分析    创建一个新的线程,指定线程要执行的任务 public static void main(String[] args) { // 开启一个新的线程 new Thread(new Runnable() { @Override public voi

  • Java 中执行动态表达式语句前中后缀Ognl、SpEL、Groovy、Jexl3

    目录 Ognl.SpEL.Groovy.Jexl3 一.前中后缀简单描述 1.前缀.中缀.后缀表达式(逆波兰表达式) 2.中缀表达式 3.后缀表达式 4.前缀表达式 二.OGNL 三.SpEL 四.Jexl/Jexl3 五.Groovy 六.扩展 Ognl.SpEL.Groovy.Jexl3 在一些规则集或者工作流项目中,经常会遇到动态解析表达式并执行得出结果的功能. 规则引擎是一种嵌入在应用程序中的组件,它可以将业务规则从业务代码中剥离出来,使用预先定义好的语义规范来实现这些剥离出来的业务规则

  • java8中的lambda表达式,看这篇绝对够

    目录 Lambda表达式 特性 一.lambda表达式介绍 1.1 lambda表达式结构 1.2 常见的Lambda表达式 1.3 基本语法 1.4 类型检查 1.5 类型推断 1.6 变量作用域 1.7 方法引用 1.8 构造器引用 二.在何处使用lambda表达式 2.1 函数式接口介绍 2.2 常见的函数式接口 2.3 常见的Lambda和已有的实现 2.4 针对装箱拆箱的优化 2.5 复合Lambda函数 Lambda表达式 Lambda是简洁的标识可传递匿名函数的一种方式.“互动”事

随机推荐