美文网首页
JAVA的函数式接口

JAVA的函数式接口

作者: 湘西刺客王胡子 | 来源:发表于2019-12-13 18:23 被阅读0次

    函数式编程

      函数式编程(英语:functional programming)或称函数程序设计泛函编程,是一种编程范式,它将电脑运算视为函数运算,并且避免使用程序状态以及易变对象
      比起指令式编程,函数式编程更加强调程序执行的结果而非执行的过程,倡导利用若干简单的执行单元让计算结果不断渐进,逐层推导复杂的运算,而不是设计一个复杂的执行过程。
      纯函数式编程语言通常不允许直接使用程序状态以及易变对象

    函数式编程不需要考虑"死锁"(deadlock),因为它不修改变量,所以根本不存在"锁"线程的问题。不必担心一个线程的数据,被另一个线程修改,所以可以很放心地把工作分摊到多个线程,部署"并发编程"

    OO(object oriented,面向对象)是抽象数据,FP(functional programming,函数式编程)是抽象行为。

    Lambda表达式

    1. Lambda 表达式是使用最小可能语法编写的函数定义:

    2. Lambda 表达式产生函数,而不是类。 在 JVM(Java Virtual Machine,Java 虚拟机)上,一切都是一个类,因此在幕后执行各种操作使 Lambda 看起来像函数 —— 但作为程序员,你可以高兴地假装它们“只是函数”。

    3. Lambda 语法尽可能少,这正是为了使 Lambda 易于编写和使用。

    函数式接口

    方法引用和 Lambda 表达式必须被赋值,同时编译器需要识别类型信息以确保类型正确。 Lambda 表达式特别引入了新的要求。 代码示例:

    x -> x.toString()
    

    我们清楚这里返回类型必须是 String,但 x 是什么类型呢?

    Lambda 表达式包含类型推导(编译器会自动推导出类型信息,避免了程序员显式地声明)。编译器必须能够以某种方式推导出 x 的类型。

    下面是第 2 个代码示例:

    (x, y) -> x + y
    

    现在 xy 可以是任何支持 + 运算符连接的数据类型,可以是两个不同的数值类型或者是 1 个 String 加任意一种可自动转换为 String 的数据类型(这包括了大多数类型)。 但是,当 Lambda 表达式被赋值时,编译器必须确定 xy 的确切类型以生成正确的代码。

    该问题也适用于方法引用。 假设你要传递 System.out :: println 到你正在编写的方法 ,你怎么知道传递给方法的参数的类型?

    为了解决这个问题,Java 8 引入了 java.util.function 包。它包含一组接口,这些接口是 Lambda 表达式和方法引用的目标类型。 每个接口只包含一个抽象方法,称为函数式方法。

    在编写接口时,可以使用 @FunctionalInterface 注解强制执行此“函数式方法”模式。一个 函数式接口 即使不加 @FunctionalInterface 注解,也可以与lambda配合使用,但这样的函数式接口是 容易出错 的:如有某个人在接口定义中增加了另一个方法,这时,这个接口就不再是函数式的了,并且编译过程也会失败。为了克服函数式接口的这种 脆弱性 并且能够 明确声明 接口作为函数式接口的意图,建议显式使用该注解 。

    java.util.function 包旨在创建一组完整的目标接口,使得我们一般情况下不需再定义自己的接口。这主要是因为基本类型会产生一小部分接口。 如果你了解命名模式,顾名思义就能知道特定接口的作用。

    以下是基本命名准则:

    1. 如果只处理对象而非基本类型,名称则为‘Supplier’,ConsumerPredicate,‘Operator’,Function 等。参数类型通过泛型添加。

    2. 如果接收的参数是基本类型,则由名称的第一部分表示,如 LongConsumerDoubleFunctionIntPredicate 等,但基本 Supplier 类型例外。

    3. 如果返回值为基本类型,则用 To 表示,如 ToLongFunction <T>IntToLongFunction

    4. 如果返回值类型与参数类型一致,则是一个运算符:单个参数使用 UnaryOperator,两个参数使用 BinaryOperator

    5. 如果接收两个参数且返回值为布尔值,则是一个谓词(Predicate)。

    6. 如果接收的两个参数类型不同,则名称中有一个 Bi

    下表描述了 java.util.function 中的目标类型(包括例外情况):

    特征 函数式方法名 示例
    无参数;
    无返回值
    Runnable
    (java.lang)
    run()
    Runnable
    无参数;
    返回类型任意
    Supplier
    get()
    getAs类型()
    Supplier<T>
    BooleanSupplier
    IntSupplier
    LongSupplier
    DoubleSupplier
    无参数;
    返回类型任意
    Callable
    (java.util.concurrent)
    call()
    Callable<V>
    1 参数;
    无返回值
    Consumer
    accept()
    Consumer<T>
    IntConsumer
    LongConsumer
    DoubleConsumer
    2 参数 Consumer BiConsumer
    accept()
    BiConsumer<T,U>
    2 参数 Consumer
    1 引用;
    1 基本类型
    Obj类型Consumer
    accept()
    ObjIntConsumer<T>
    ObjLongConsumer<T>
    ObjDoubleConsumer<T>
    1 参数;
    返回类型不同
    Function
    apply()
    To类型类型To类型
    applyAs类型()
    Function<T,R>
    IntFunction<R>
    LongFunction<R>
    DoubleFunction<R>
    ToIntFunction<T>
    ToLongFunction<T>
    ToDoubleFunction<T>
    IntToLongFunction
    IntToDoubleFunction
    LongToIntFunction
    LongToDoubleFunction
    DoubleToIntFunction
    DoubleToLongFunction
    1 参数;
    返回类型相同
    UnaryOperator
    apply()
    UnaryOperator<T>
    IntUnaryOperator
    LongUnaryOperator
    DoubleUnaryOperator
    2 参数类型相同;
    返回类型相同
    BinaryOperator
    apply()
    BinaryOperator<T>
    IntBinaryOperator
    LongBinaryOperator
    DoubleBinaryOperator
    2 参数类型相同;
    返回整型
    Comparator
    (java.util)
    compare()
    Comparator<T>
    2 参数;
    返回布尔型
    Predicate
    test()
    Predicate<T>
    BiPredicate<T,U>
    IntPredicate
    LongPredicate
    DoublePredicate
    参数基本类型;
    返回基本类型
    类型To类型Function
    applyAs类型()
    IntToLongFunction
    IntToDoubleFunction
    LongToIntFunction
    LongToDoubleFunction
    DoubleToIntFunction
    DoubleToLongFunction
    2 参数类型不同 Bi操作
    (不同方法名)
    BiFunction<T,U,R>
    BiConsumer<T,U>
    BiPredicate<T,U>
    ToIntBiFunction<T,U>
    ToLongBiFunction<T,U>
    ToDoubleBiFunction<T>

    此表仅提供些常规方案。通过上表,你应该或多或少能自行推导出更多行的函数式接口。

    按此命名规范,可以举一些声明示例:

    
            Runnable runnable = () -> {};
            Callable<Integer> callable = () -> (new Random().nextInt());
    
            Supplier<Integer> supplier = () -> (new Random().nextInt());
            IntSupplier intSupplier = () -> (new Random().nextInt());
    
            Consumer<String> consumer = s -> System.out.print(s);
            Consumer<String> consumer1 = System.out::println;
            BiConsumer<Integer,Long> biConsumer = (i,l) -> System.out.print(i+l);
            ObjIntConsumer<Result> objIntConsumer = (r,i) -> System.out.print(r.getMessage() + i);
    
            //Operator只支持基本类型
            UnaryOperator<Integer> unaryOperator = i -> i++;
            BinaryOperator<Integer> binaryOperator = (i1,i2) -> i1+i2;
    
            //因为一般都用来比较引用类型,所以没有基本类型的命名方式
            //如果不是用作比较目的,可以使用Function
            Comparator<Result> comparator = (r1,r2) -> r1.getCode() - r2.getCode(); //Comparator.comparingInt(Result::getCode)
    
            Predicate<Integer> predicate = i -> i > 0;
            IntPredicate intPredicate = i -> i > 0;
            BiPredicate<Integer,Result> biPredicate = (i,r) -> i > r.getCode();
    
    
            Function<String,Integer> function =  s -> Integer.parseInt(s); //Integer::parseInt
            ToIntFunction<String> toIntFunction = Integer::parseInt;
            IntFunction<String> intFunction = Integer::toString;
            BiFunction<Integer,Long,String> biFunction = (i,l) -> "" + i + l;
    

    相关文章

      网友评论

          本文标题:JAVA的函数式接口

          本文链接:https://www.haomeiwen.com/subject/moofnctx.html