Java 函数编程详细介绍

目录
  • 一、函数编程Lambda
    • 1、接口
  • 二、Java函数接口
    • 1、Predicate
    • 2、Consumer
    • 3、Function
    • 4、Supplier
  • 三、类型检查
    • 1、Capturing Lambda
  • 四、方法引用
    • 1、构造函数引用
    • 2、组合Lambda
    • 3、Comparators
    • 4、Functions
  • 五、总结

前言:

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

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

一、函数编程Lambda

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

(parameters) -> expression

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

1、接口

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

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

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

函数描述符:

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

二、Java函数接口

1、Predicate

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

函数描述符: T->boolean

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

2、Consumer

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

函数描述符T->void

3、Function

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

函数描述符T->R

4、Supplier

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

函数描述符()->R

Primitive Specializations

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

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

三、类型检查

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

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

1、Capturing Lambda

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

我们为何有这个限制?

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

四、方法引用

有三种主要的方法参考:

  • 对静态方法的方法引用。例如,–Integer::parseInt
  • 对任意类型的实例方法的方法引用。示例–String::length
  • 对现有对象或表达式的实例方法的方法引用。示例–student::getRank,其中student是具有方法getRankstudent类型的局部变量
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);

1、构造函数引用

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

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

2、组合Lambda

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

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

3、Comparators

按逆序排列学生

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

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

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

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

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

4、Functions

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

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"));

五、总结

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

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

(0)

相关推荐

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

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

  • 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

  • Java8新特性:函数式编程

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

  • Java 函数式编程要点总结

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

  • 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.接口 二.Java函数接口 1.Predicate 2.Consumer 3.Function 4.Supplier 三.类型检查 1.Capturing Lambda 四.方法引用 1.构造函数引用 2.组合Lambda 3.Comparators 4.Functions 五.总结 前言: 函数式编程是一种编程范式,其中程序是通过应用和组合函数来构造的.它是一种声明式编程范式,其中函数定义是表达式树,每个表达式树返回一个值,而不是一系列改变程序状态的命令语句

  • java RMI详细介绍及实例讲解

    java本身提供了一种RPC框架--RMI(即RemoteMethodInvoke远程方法调用),在编写一个接口需要作为远程调用时,都需要继承了Remote,Remote接口用于标识其方法可以从非本地虚拟机上调用的接口,只有在"远程接口"(扩展java.rmi.Remote的接口)中指定的这些方法才可远程使用,下面通过一个简单的示例,来讲解RMI原理以及开发流程: 为了真正实现远程调用,首先创建服务端工程rmi-server,结构如下: 代码说明: 1.User.java:用于远程调用

  • Java超详细介绍抽象类与接口的使用

    目录 1.抽象类的语法和特性 1.1语法 1.2特性 2.接口的语法和使用 2.1语法 2.2特性 1.抽象类的语法和特性 1.1语法 1.在Java中,一个类如果被abstract 修饰称为抽象类,抽象类中被 abstract 修饰的方法称为抽象方法,抽象方法不用给出具体的实现体. // 抽象类:被abstract修饰的类 public abstract class Shape { // 抽象方法:被abstract修饰的方法,没有方法体 abstract public void draw()

  • Java超详细介绍封装与访问控制修符

    概念:我们在写入一个类的时候,为了保护里边的属性不被随意的调用这是我们可以使用特殊的修饰符进行相应的保护,而这样的话我们似乎只能在该类中调用使用了,出现某些特殊情况时就会无法发调用,虽然挺高了安全性但也降低了灵活性,这个时候我们的包装类就出现了,我们通过对某个方法的进行特殊方法的包装来对其进行相应的调用与赋值.就相当于银行为了保护财产会选择将金钱放进保险柜中来确保其的安全,但是当我们要取钱时,银行就要拿钥匙打开保险柜.修饰符相当于银行的保险柜,封装相当于保险柜的钥匙. 访问修饰符如下: 1) p

  • JAVA HashMap详细介绍和示例

    第1部分 HashMap介绍HashMap简介HashMap 是一个散列表,它存储的内容是键值对(key-value)映射.HashMap 继承于AbstractMap,实现了Map.Cloneable.java.io.Serializable接口.HashMap 的实现不是同步的,这意味着它不是线程安全的.它的key.value都可以为null.此外,HashMap中的映射不是有序的.HashMap 的实例有两个参数影响其性能:"初始容量" 和 "加载因子".容量

  • JAVA ArrayList详细介绍(示例)

    第1部分 ArrayList介绍ArrayList 是一个数组队列,相当于 动态数组.与Java中的数组相比,它的容量能动态增长.它继承于AbstractList,实现了List, RandomAccess, Cloneable, java.io.Serializable这些接口.ArrayList 继承了AbstractList,实现了List.它是一个数组队列,提供了相关的添加.删除.修改.遍历等功能.ArrayList 实现了RandmoAccess接口,即提供了随机访问功能.Randmo

  • Java注解详细介绍

    Java注解介绍 基于注解(Annotation-based)的Java开发无疑是最新的开发趋势.[译者注: 这是05年的文章,在2014年,毫无疑问,多人合作的开发,使用注解变成很好的合作方式,相互之间的影响和耦合可以很低]. 基于注解的开发将Java开发人员从繁琐笨重的配置文件中解脱出来. Java 5.0中首次引入了注解,注解是这个JDK版本的特性之一,将程序员书写Java示例API文件的工作转交给了编译器. 当不再维护分开的源代码和API文档后,代码和文档会更容易维护. 生成的代码示例也

  • JAVA Stack详细介绍和示例学习

    第1部分 Stack介绍Stack是栈.它的特性是:先进后出(FILO, First In Last Out).java工具包中的Stack是继承于Vector(矢量队列)的,由于Vector是通过数组实现的,这就意味着,Stack也是通过数组实现的,而非链表.当然,我们也可以将LinkedList当作栈来使用! Stack的继承关系Stack和Collection的关系如下图:Stack的构造函数Stack只有一个默认构造函数,如下: 复制代码 代码如下: Stack() Stack的API

  • 浅析Java getResource详细介绍

    在 Java 中访问资源我们一般使用 getResource() 方法,亦或者直接new File()然后传入一个文件路径获取资源文件.但是这两者究竟有什么区别呢?由于平常在使用的时候经常会傻傻分不清楚,因此这里写篇博客整理下我的笔记. Java 运行中资源的说明 这里我们将运行中 Java 进程可以获取到的资源简单分为两类: 存在于文件系统中的资源 这类资源一般直接存在与磁盘中,我们可以直接从资源管理器中访问到.当我们通过应用访问这类资源时既可以使用 File对象通过文件系统获取到,也可以使用

  • java异常处理详细介绍及实例

    Java异常层次结构 Exception异常 RuntimeException与非RuntimeException异常的区别: 非RuntimeException(检查异常):在程序中必须使用try-catch进行处理,否则程序无法编译. RuntimeException:可以不使用try-catch进行处理,但是如果有异常产生,则异常将由JVM进行处理. 比如:我们从来没有人去处理过NullPointerException异常,它就是运行时异常,并且这种异常还是最常见的异常之一. 出现运行时异

随机推荐