Java中的函数式编程

目录
  • 1、Lambda
  • 2、函数接口
    • 2.1 函数描述符
  • 3、Java函数接口
    • 3.1 Predicate
    • 3.2 Consumer
    • 3.3 Function
    • 3.4 Supplier
    • 3.5 Primitive Specializations
  • 4、类型检查
    • 4.1 Capturing Lambda
  • 5、方法引用
    • 5.1 构造函数引用
    • 5.2 组合Lambda
    • 5.3 Comparators
    • 5.4 Predicates
    • 5.5 Functions
  • 6、总结

函数式编程是一种编程范式,其中程序是通过应用和组合函数来构造的。它是一种声明式编程范式,其中函数定义是表达式树,每个表达式树返回一个值,而不是一系列改变程序状态的命令语句

Java8引入了Lambda形式的函数式编程。术语Lambda来自Lambda演算,用于描述计算。

1、Lambda

我们可以将lambda表达式视为一个匿名函数,可以将其分配给变量并传递给方法,该方法接受函数接口作为参数。Lambda表达式没有名称,但它有一个参数列表、一个主体和一个返回类型。

(parameters) -> expression

lambda表达式可以在函数接口的上下文中使用。

2、函数接口

函数接口是只指定一个抽象方法的接口。

public interface Comparator<T> {
    int compare(T o1, T o2);
}
public interface Runnable {
    void run();
}

Lambda表达式允许我们直接内联提供函数接口的抽象方法的实现,并将整个表达式视为函数接口的实例。

2.1 函数描述符

我们将函数接口的抽象方法的签名称为函数描述符。函数描述符描述lambda表达式的签名。例如,我们可以将 Runnable 的函数描述符看作 ()->void ,因为它有一个抽象方法,不接受任何内容,也不返回任何内容(void)。

3、Java函数接口

3.1 Predicate

Predicate<T> 接口定义了一个名为 test 的抽象方法,该方法接受一个泛型类型为 T 的对象并返回一个布尔值。此接口可用于表示使用T类型对象的布尔表达式。

函数描述符: T->boolean

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}

3.2 Consumer

java.util.function.Consumer<T> 接口定义了一个名为 accept 的抽象方法,该方法接受一个泛型类型为 T 的对象,并且不返回任何结果( void )。当我们需要访问T类型的对象并对其执行某些操作时,可以使用此接口。

函数描述符: T->void

3.3 Function

java.util.function.function<T,R> 接口定义了一个名为 apply 的抽象方法,该方法将一个泛型类型为 T 的对象作为输入,并返回一个泛型类型为 R 的对象。当我们需要定义一个lambda将信息从输入对象映射到输出时,可以使用该接口。

函数描述符: T->R

3.4 Supplier

接口 java.util.function.Supplier<T> 定义了一个名为 get 的抽象方法,该方法不接受任何内容并返回类型为T的对象。

函数描述符: ()->R

3.5 Primitive Specializations

原语接口是专用接口,用于在输入或输出为原语时避免自动装箱操作。

public interface IntPredicate {
    boolean test(int t);
}

4、类型检查

lambda的类型是从使用lambda的上下文中推导出来的。上下文中lambda表达式所需的类型(例如,传递给它的方法参数或分配给它的局部变量)称为目标类型。Lambda表达式可以从赋值上下文、方法调用上下文(参数和返回)和强制转换上下文中获取其目标类型。

Object o = (Runnable) () -> System.out.println("Hello");

4.1 Capturing Lambda

lambda可以不受限制地捕获(在其主体中引用)实例变量和静态变量。但是当捕获局部变量时,它们必须显式地声明为 final 或实际上是 final

我们为何有这个限制?

实例变量存储在堆上,而局部变量位于堆栈上。如果lambda可以直接访问局部变量,并且lambda在线程中使用,那么使用lambda的线程可以在分配变量的线程解除分配变量后尝试访问该变量。因此,Java将对自由局部变量的访问实现为对其副本的访问,而不是对原始变量的访问。如果局部变量只分配给一次,则这没有什么区别,因此存在限制。

5、方法引用

有三种主要的方法参考:

  • 对静态方法的方法引用。例如, – Integer::parseInt
  • 对任意类型的实例方法的方法引用。示例– String::length
  • 对现有对象或表达式的实例方法的方法引用。示例– student::getRank ,其中 student 是具有方法 getRank() student 类型的局部变量
List<String> list = Arrays.asList("a","b","A","B");
list.sort((s1, s2) -> s1.compareToIgnoreCase(s2));

可以写成

List<String> list = Arrays.asList("a","b","A","B");
list.sort(String::compareToIgnoreCase);

5.1 构造函数引用

可以使用 ClassName::new 引用现有构造函数

Supplier<List<String>> supplier = ArrayList::new ;与 Supplier<List<String>> supplier = () -> new ArrayList<>() 相同;

5.2 组合Lambda

许多函数接口包含可用于组合lambda表达式的默认方法。组合示例-

将两个谓词组合成一个较大的谓词,在两个谓词之间执行or操作
反向或链式比较器

5.3 Comparators

按逆序排列学生

Comparator<Student> c = Comparator.comparing(Student::getRank);
students.sort(comparing(Student::getRank).reversed());

根据姓名(反向)对学生进行排序,然后按反向顺序排列

students.sort(comparing(Student::getName).reversed()
        .thenComparing(Student::getRank));

5.4 Predicates

Predicates接口包括三个方法: negate , and , 和 or ,可用于创建更复杂的谓词。

Predicate<Integer> naturalNumber = i -> i > 0;
Predicate<Integer> naturalNumberLessThanHundred = naturalNumber.and( i -> i < 100);

5.5 Functions

函数接口带有两个默认方法, andThen compose

Consider f(x) = x2 and g(x) = x3 + 1 then

g(f(x)) ->

Function<Integer,Integer> square = n -> n*n;
Function<Integer,Integer> squareAndCube = square.andThen(n -> n*n*n+1);
System.out.println(squareAndCube.apply(2));
65

f(g(x)) ->

Function<Integer,Integer> square = n -> n*n;
Function<Integer,Integer> squareAndCube = square.compose(n -> n*n*n + 1);
System.out.println(squareAndCube.apply(2));

应用Lambda

让我们看看如何编写一个通用方法来根据 veratain 属性过滤一组书籍(将其视为sqlwhere子句)。

public static List<Book> filter(Predicate<Book> where) {
  List<Book> books = Catalogue.books();
  return books.stream().filter(where).collect(Collectors.toList());
}

Lambda表达式通过不同的过滤器过滤不同的书籍

List<Book> javaBook = filter(book -> book.getCategory().equals(JAVA));
List<Book> joshuaBlochBook = filter(book -> book.getAuthor().equals("Joshua Bloch"));

6、总结

lambda表达式可以被认为是一个匿名函数,可以在函数接口的上下文中使用。函数接口是只指定一个抽象方法的接口。

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

(0)

相关推荐

  • Java8新特性:函数式编程

    首先需要清楚一个概念:函数式接口:它指的是有且只有一个未实现的方法的接口,一般通过FunctionalInterface这个注解来表明某个接口是一个函数式接口.函数式接口是Java支持函数式编程的基础. 1 Java8函数式编程语法入门 Java8中函数式编程语法能够精简代码. 使用Consumer作为示例,它是一个函数式接口,包含一个抽象方法accept,这个方法只有输入而无输出. 现在我们要定义一个Consumer对象,传统的方式是这样定义的: Consumer c = new Consum

  • 详解JAVA 函数式编程

    1.函数式接口 1.1概念: java中有且只有一个抽象方法的接口. 1.2格式: 修饰符 interface 接口名称 { public abstract 返回值类型 方法名称(可选参数信息); // 其他非抽象方法内容 } //或者 public interface MyFunctionalInterface { void myMethod(); } 1.3@FunctionalInterface注解: 与 @Override 注解的作用类似,Java 8中专门为函数式接口引入了一个新的注解

  • JAVA8之函数式编程Function接口用法

    从这章开始,会介绍几个常用的函数式接口工具,首先先来看下这个大家族: 首先从Function接口开始介绍 一. 概述 该接口顾名思义,函数的意思,就像是数学,是给定一个参数然后返回结果.该类方法如下: package java.util.function; import java.util.Objects; @FunctionalInterface public interface Function<T, R> { R apply(T t); default <V> Functio

  • Java 函数式编程要点总结

    目录 一.函数式概念 二.函数与方法 三.JDK函数基础 1.Lambda表达式 2.函数式接口 四.Optional类 1.Null判断 2.Optional应用 五.Stream流 六.源代码地址 一.函数式概念 函数式编程是一种结构化编程的范式,主要思想是把运算过程尽量写成系列嵌套的函数调用.函数编程的概念表述带有很抽象的感觉,可以基于案例看: public class Function01 {     public static void main(String[] args) {   

  • 深入了解java 8的函数式编程

    前言 关于"Java 8为Java带来了函数式编程"已经有了很多讨论,但这句话的真正意义是什么? 本文将讨论函数式,它对一种语言或编程方式意味着什么.在回答"Java 8的函数式编程怎么样"之前,我们先看看Java的演变,特别是它的类型系统,我们将看到Java 8的新特性,特别是Lambda表达式如何改变Java的风景,并提供函数式编程风格的主要优势. 函数式编程语言是什么? 函数式编程语言的核心是它以处理数据的方式处理代码.这意味着函数应该是第一等级(First-

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

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

  • java中lambda(函数式编程)一行解决foreach循环问题

    java lambda(函数式编程)一行解决foreach循环 首先给大家推荐<精通lambda表达式:java多核编程> 这本书详细介绍了lambda表达式从入门到理解.应用 下面介绍用以前的循环方式进行对比,来更加清晰地java函数式编程中foreach的用法 一.以前我们使用的for循环 /** * for循环 */ @Test public void forTest() { // 实例化一个List List<Point> points = Arrays.asList(ne

  • 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中的函数式编程

    目录 1.Lambda 2.函数接口 2.1 函数描述符 3.Java函数接口 3.1 Predicate 3.2 Consumer 3.3 Function 3.4 Supplier 3.5 Primitive Specializations 4.类型检查 4.1 Capturing Lambda 5.方法引用 5.1 构造函数引用 5.2 组合Lambda 5.3 Comparators 5.4 Predicates 5.5 Functions 6.总结 函数式编程是一种编程范式,其中程序是

  • 利用Fn.py库在Python中进行函数式编程

    尽管Python事实上并不是一门纯函数式编程语言,但它本身是一门多范型语言,并给了你足够的自由利用函数式编程的便利.函数式风格有着各种理论与实际上的好处(你可以在Python的文档中找到这个列表): 形式上可证 模块性 组合性 易于调试及测试 虽然这份列表已经描述得够清楚了,但我还是很喜欢Michael O.Church在他的文章"函数式程序极少腐坏(Functional programs rarely rot)"中对函数式编程的优点所作的描述.我在PyCon UA 2012期间的讲座

  • Go语言中的函数式编程实践

    本文主要讲解Go语言中的函数式编程概念和使用,分享给大家,具体如下: 主要知识点: Go语言对函数式编程的支持主要体现在闭包上面 闭包就是能够读取其他函数内部变量的函数.只有函数内部的子函数才能读取局部变量,所以闭包可以理解成"定义在一个函数内部的函数".在本质上,闭包是将函数内部和函数外部连接起来的桥梁. 学习闭包的基本使用 标准的闭包具有不可变性:不能有状态,只能有常量和函数,而且函数只能有一个参数,但是一般可以不用严格遵守 使用闭包 实现 斐波那契数列 学习理解函数实现接口 使用

  • JavaScript中的函数式编程详解

    函数式编程 函数式编程是一种编程范式,是一种构建计算机程序结构和元素的风格,它把计算看作是对数学函数的评估,避免了状态的变化和数据的可变,与函数式编程相对的是命令式编程.我们有这样一个需求,给数组的每个数字加一: // 数组每个数字加一, 命令式编程 let arr = [1, 2, 3, 4]; let newArr = []; for(let i = 0; i < arr.length; i++){ newArr.push(arr[i] + 1); } console.log(newArr)

  • Java中线程休眠编程实例

    import java.awt.*; import java.util.*; import javax.swing.*; public class SleepMethodTest extends JFrame { /** * */ private static final long serialVersionUID = 1L; private Thread t; // 定义颜色数组 private static Color[] color = { Color.BLACK, Color.BLUE,

  • 详解Java 中的函数式接口

    目录 @FunctionalInterface注解 最简单的函数式接口 基础数据类型的函数表达式 二元输入参数的函数 Two-Arity Function Specializations Suppliers 供给型接口 & Consumers 消费型接口 Predicates 断言型接口 Operators 总结 @FunctionalInterface注解 如果你想自己定义个新的函数式接口,强烈建议你加上*@FunctionalInterface* 注解.可以更好地揭示我们定义这个接口的意思,

  • 举例讲解Java中的多线程编程

    Java创建线程(Runnable接口和Thread类) 大多数情况,通过实例化一个Thread对象来创建一个线程.Java定义了两种方式: 实现Runnable 接口: 可以继承Thread类. 下面的依次介绍了每一种方式. 实现Runnable接口 创建线程的最简单的方法就是创建一个实现Runnable 接口的类.Runnable抽象了一个执行代码单元.你可以通过实现Runnable接口的方法创建每一个对象的线程.为实现Runnable 接口,一个类仅需实现一个run()的简单方法,该方法声

  • Python中的函数式编程:不可变的数据结构

    让我们首先考虑正方形和长方形.如果我们认为在接口方面,忽略了实现细节,方块是否是矩形的子类型? 子类型的定义取决于Liskov代换原理.为了成为一个子类型,它必须能够完成超级类型所做的一切. 如何定义矩形的接口? zope.interface import Interface class IRectangleInterface: get_length: """Squares can do that""" get_width: "&quo

随机推荐