Java8 添加了一个新的特性Function,顾名思义这一定是一个函数式的操作。我们知道Java8的最大特性就是函数式接口。所有标注了@FunctionalInterface注解的接口都是函数式接口,具体来说,所有标注了该注解的接口都将能用在lambda表达式上。

标注了@FunctionalInterface的接口有很多,但此篇我们主要讲Function,了解了Function其他的操作也就很容易理解了。

@FunctionalInterface
public interface Function<T, R> {R apply(T t);/*** @return a composed function that first applies the {@code before}* function and then applies this function*/default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {Objects.requireNonNull(before);return (V v) -> apply(before.apply(v));}/*** @return a composed function that first applies this function and then* applies the {@code after} function*/default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {Objects.requireNonNull(after);return (T t) -> after.apply(apply(t));}
}

为了方便地阅读源码,我们需要了解一些泛型的知识,如果你对泛型已经很熟悉了,那你可以跳过这段 。

泛型是JDK1.5引入的特性,通过泛型编程可以使编写的代码被很多不同的类型所共享,这可以很好的提高代码的重用性。因为本篇重点不是介绍泛型,所以我们只关注上述Function源码需要用到的泛型含义。

1. 泛型类

泛型类使用<T>来表示该类为泛型类,其内部成员变量和函数的返回值都可以为泛型<T> ,Function源码的标识为<T,R>,也就是两个泛型参数,此处不再赘述,具体泛型类可以看网上的文章。

2. 泛型方法和通配符

在方法修饰符的后面加一个<T>表明该方法为泛型方法,如Function 的源码里的compose方法的<V>。通配符也很好理解,还是compose的例子,我们可以看到compose的参数为一个Function类型,其中Functin的参数指定了其第一个参数必须是V的父类,第二个参数必须继承T,也就是T的子类。

源码解析

1.apply

讲完了上面这些就可以开始研究源码了。

首先我们已经知道了Function是一个泛型类,其中定义了两个泛型参数T和R,在Function中,T代表输入参数,R代表返回的结果。也许你很好奇,为什么跟别的java源码不一样,Function 的源码中并没有具体的逻辑呢?

其实这很容易理解,Function 就是一个函数,其作用类似于数学中函数的定义 ,(x,y)跟<T,R>的作用几乎一致。

所以Function中没有具体的操作,具体的操作需要我们去为它指定,因此apply具体返回的结果取决于传入的lambda表达式。

 R apply(T t);

举个例子:

public void test(){Function<Integer,Integer> test=i->i+1;test.apply(5);
}
/** print:6*/

我们用lambda表达式定义了一个行为使得i自增1,我们使用参数5执行apply,最后返回6。这跟我们以前看待Java的眼光已经不同了,在函数式编程之前我们定义一组操作首先想到的是定义一个方法,然后指定传入参数,返回我们需要的结果。函数式编程的思想是先不去考虑具体的行为,而是先去考虑参数,具体的方法我们可以后续再设置。

再举个例子:

public void test(){Function<Integer,Integer> test1=i->i+1;Function<Integer,Integer> test2=i->i*i;System.out.println(calculate(test1,5));System.out.println(calculate(test2,5));
}
public static Integer calculate(Function<Integer,Integer> test,Integer number){return test.apply(number);
}
/** print:6 */
/** print:25 */

我们通过传入不同的Function,实现了在同一个方法中实现不同的操作。在实际开发中这样可以大大减少很多重复的代码,比如我在实际项目中有个新增用户的功能,但是用户分为VIP和普通用户,且有两种不同的新增逻辑。那么此时我们就可以先写两种不同的逻辑。除此之外,这样还让逻辑与数据分离开来,我们可以实现逻辑的复用

当然实际开发中的逻辑可能很复杂,比如两个方法F1,F2都需要操作AB,但是F1需要A->B,F2方法需要B->A。这样的我们用刚才的方法也可以实现,源码如下:

public void test(){Function<Integer,Integer> A=i->i+1;Function<Integer,Integer> B=i->i*i;System.out.println("F1:"+B.apply(A.apply(5)));System.out.println("F2:"+A.apply(B.apply(5)));
}
/** F1:36 */
/** F2:26 */

也很简单呢,但是这还不够复杂,假如我们F1,F2需要四个逻辑ABCD,那我们还这样写就会变得很麻烦了。

2.compose和andThen

compose和andThen可以解决我们的问题。先看compose的源码

  default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {Objects.requireNonNull(before);return (V v) -> apply(before.apply(v));}

compose接收一个Function参数,返回时先用传入的逻辑执行apply,然后使用当前Function的apply。

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {Objects.requireNonNull(after);return (T t) -> after.apply(apply(t));}

andThen跟compose正相反,先执行当前的逻辑,再执行传入的逻辑。

这样说可能不够直观,我可以换个说法给你看看

compose等价于B.apply(A.apply(5)),而andThen等价于A.apply(B.apply(5))。

public void test(){Function<Integer,Integer> A=i->i+1;Function<Integer,Integer> B=i->i*i;System.out.println("F1:"+B.apply(A.apply(5)));System.out.println("F1:"+B.compose(A).apply(5));System.out.println("F2:"+A.apply(B.apply(5)));System.out.println("F2:"+B.andThen(A).apply(5));
}
/** F1:36 */
/** F1:36 */
/** F2:26 */
/** F2:26 */

我们可以看到上述两个方法的返回值都是一个Function,这样我们就可以使用建造者模式的操作来使用。

B.compose(A).cpmpose(A).andThen(A).apply(5);

这个操作很简单,你可以自己试试。

【Java8】Function 讲解相关推荐

  1. SQL 函数 function 讲解+代码实例

    文章目录 1. 对存储程序的说明 2. 创建函数 create function 3. 调用函数 select 4. 查看函数 4.1. 查看函数状态 show status 4.2. 查看函数定义 ...

  2. Java8 Function.identity() 的使用

    一.简单介绍 话不多说,直接上JDK源码: static Function identity() {return t -> t; } 我们可以看到,Function.identity() 的作用 ...

  3. java8 Function函数式接口学习

    /*** 表示一个函数,它接收一个参数并且返回一个结果.* 这是一个函数式接口,它有一个重要的方法是apply(Object)* @param <T> 函数的输入类型* @param &l ...

  4. java8 function 固定0_Java8特性使用Function代替分支语句

    传统的多分支方式(圈复杂度为6): public String order(String type) { if ("1".equals(type)) { return " ...

  5. 0.18/0.13um的逻辑相关step function 讲解

    ZEROOXIDE 的作用是什么? 第一是为后序的ZERO PHOTO时做PR的隔离,防止PR直接与Si接触,造成污染. PR中所含的有机物很难清洗. 第二,WAFTER MARK是用激光来打的,在S ...

  6. java8的函数指针

    转载自  java8的函数指针 这段时间 项目实在是太紧了,没有时间写博客啊.java模式的文章还没有写完,后期在写吧. 最近项目中大量的使用到了远程调用以及回调.是采用反射的方式实现的.担心到大量的 ...

  7. Java8 通关攻略

    Java8早在2014年3月就发布了,7年了,你有对它做个全面的了解吗 本文是用我拙劣的英文和不要脸的这抄抄那抄抄,熬出来的,没有深究源码,只是对 Java8 有一个整体的认知,可以上手用起来,示例代 ...

  8. JAVA8 BiConsumer 接口

    这个接口跟<JAVA8 Consumer接口>很像,表达的想法也是一致的,都是消费的意思,我们先看下接口定义 @FunctionalInterface public interface B ...

  9. 视频教程-JavaScript实战讲解课程-Java

    JavaScript实战讲解课程 Java讲师,多年培训经验,曾参与开发过的项目:某省电信ODS系统.电信自助取数系统.CRM客户关系管理系统.环境应急信息系统.自来水客服系统等.熟练掌握J2EE平台 ...

  10. SQL 存储过程 procedure 讲解+代码实例

    文章目录 1. 存储过程概述 2. 创建存储过程 create procedure 2.1. 参数 in.out.inout 3. 调用存储过程 call 4. 查看存储过程 4.1. 查看存储过程的 ...

最新文章

  1. jsp的session介绍 (转)
  2. dpkg安装软件流程_ubuntu安装搜狗输入法linux版
  3. Caffe 学习笔记1
  4. redis java 性能_Redis 性能优化
  5. linux 解决man命令输出到文档打开乱码问题
  6. element显示true或者false_element-ui轮播的简单实现
  7. ES6的变量声明详述
  8. 国密SM4对称算法实现说明(原SMS4无线局域网算法标准)
  9. oracle 最大一行,一行最大column数和row piece-概念
  10. Mac里的airdrop传输文件
  11. 基于51单片机中文汉字LCD12864滚动显示屏仿真(源码+仿真+全套资料)
  12. 机器学习基础教程——最小二乘法案列
  13. [渝粤教育] 西南科技大学 会计电算化 在线考试复习资料2021版
  14. 关于“堆栈”的含义及理解
  15. jy-09-SERVLETJSP——Servlet-Cookie-Session
  16. React Reflux
  17. c语言中符号是什么作用是什么,c语言中的符号|=是什么意思?
  18. 2022-2028年中国离岸金融行业市场全景调查及投资潜力研究报告
  19. 串口转以太网模块:WIZ108SR(用户手册)
  20. R中的Box-Cox变换

热门文章

  1. 绘制流程图的基本规则
  2. UltraISO软碟通安装与刻盘以及安装镜像
  3. 一个毕业设计手机病毒软件查杀
  4. 「音视频直播技术」Android下H264解码
  5. 《代码整洁之道》笔记整理
  6. android启动第三方浏览器问题
  7. 广州北大青鸟软件工程师班课程简介
  8. Kindle禁止自动熄屏
  9. mysql-front源码_MySQL-Front(5.3版本)
  10. php奖学金系统,java/php/net/pythont奖助学金管理系统设计