【java8新特性】02:常见的函数式接口()

  本篇文章为你整理了【java8新特性】02:常见的函数式接口()的详细内容,包含有 【java8新特性】02:常见的函数式接口,希望能帮助你了解 【java8新特性】02:常见的函数式接口。

  Jdk8提供的函数式接口都在java.util.function包下,Jdk8的函数式类型的接口都有@FunctionInterface注解所标注,但实际上即使没有该注解标注的有且只有一个抽象方法的接口,都可以算是函数式接口。
在JDK8中内置的四大核心函数式接口如下:

  
对类型为T的对象进行操作,包含方法为accpet(T t)

  如forEach、peek等方法的函数式接口都是Consumer类型

  
对类型为T的对象进行操作,返回结果为 R类型的对象,包含方法为R apply(T t)

  如map,flatMap等方法的函数式接口都是Function类型

  
确定类型为T的对象是否满足约束,并返回约束结果,包含方法为boolean test(T t)

  如filter等方法的函数式接口都是Predicate类型

  
* 消费型接口:顾名思义主要用于消费参数,不反馈调用环境(没有返回值)

   * accept: 抽象方法实现,用于调用方法。

   * andThen: 默认实现方法,内部允许我们链式调用

   public static void main(String[] args) {

   // 给定字符串转为大写并输出到控制台,匿名内部类的方式实现

   Consumer String con1 = new Consumer String () {

   @Override

   public void accept(String str) {

   System.out.println("通过匿名内部类的方式:"+ str.toUpperCase());

   // 执行该方法的时候,我们传入了给定参数字符串,它会去执行我们上述实现的accept方法并传入参数,最后执行我们给定的代码逻辑

   con1.accept("abc");

   // 给定字符串转为大写并输出到控制台,通过Lambda表达式实现

   Consumer String con2 = (text)- System.out.println("通过Lambda表达式的方式:"+ text.toUpperCase());

   con2.accept("goods");

   * 最终结果:

   * 通过匿名内部类的方式:ABC

   * 通过Lambda表达式的方式:GOOD

   * 使用lambda表达式,我们只需要记住参数列表和执行逻辑即可,其他的我们无需关注。

   }}


 

 

  

public class Main {

 

   * Consumer T

   * 消费型接口:顾名思义主要用于消费参数,不反馈调用环境(没有返回值)

   * accept: 抽象方法实现,用于调用方法。

   * andThen: 默认实现方法,内部允许我们链式调用

   public static void main(String[] args) {

   // 1.我们需要将集合进行排序后在输出到控制台

   Consumer List con1 = list- {

   System.out.println("排序前的集合:"+ list);

   Collections.sort(list);

   System.out.println("排序后的集合:"+list);

   con1.accept(Arrays.asList(1,5,3,2,9,6,7));

   * 最终结果:

   * 排序前的集合:[1, 5, 3, 2, 9, 6, 7]

   * 排序后的集合:[1, 2, 3, 5, 6, 7, 9]

   // 上面执行逻辑实现分两步,第一步需要获取到给定集合进行排序,第二个则是输出排序后的集合

   // 如果以上两个步骤分别用两个consumer也可以实现,我们可以定义一个方法接收两个consumer进行操作

   accept(Arrays.asList(1,5,3,2,9,6,7),list-

   System.out.println("andThen链式调用前集合:"+list);

   Collections.sort(list);

   },list- System.out.println("andThen链式调用后集合:"+ list));

   * 最终结果:

   * andThen链式调用前集合:[1, 5, 3, 2, 9, 6, 7]

   * andThen链式调用后集合:[1, 2, 3, 5, 6, 7, 9]

   // 如果consumer参数多个的话,我们可以直接在Lambda表达式进行链式调用,不费那劲定义方法了

   Consumer List con2 = ((Consumer List ) list - {

   System.out.println("lambda表达式的链式调用前集合:" + list);

   Collections.sort(list);

   }).andThen(list - System.out.println("lambda表达式的链式调用后集合:"+list));

   // 需要注意的是:要使用这种方式,第一个consumer要进行链式调用必须要强行指定为(Consumer)类型,后续的接口才能够调用方法

   con2.accept(Arrays.asList(1,53,31,25,99,62,17));

   * 最终结果:

   * lambda表达式的链式调用前集合:[1, 53, 31, 25, 99, 62, 17]

   * lambda表达式的链式调用后集合:[1, 17, 25, 31, 53, 62, 99]

   public static void accept(List Integer list,Consumer List con1,Consumer List con2){

   // 链式调用时会优先执行左边的接口实现,依次往右执行 我们的需求是先排序后输出,第一个Consumer是排序,第二个是输出。

   con1.andThen(con2).accept(list);

  }

 

  1.函数式接口的本质实际上就是将函数以参数的形式进行传递

  2.Consumer是消费型的函数式接口,通常用于数据内部处理,没有返回值

  3.除了Consumer之外,还有各种消费型的函数式接口,还有IntConsumer、LongConsumer等、如果需要传递两个参数则可以使用BIFunction、也可以根据自身需求进行自定义。

  Supplier T

  Consumer T 供给型函数式接口,顾名思义就是供给数据给调用环境,不接收参数传递

  

public class Main{

 

   * 供给型函数式接口顾名思义就是顾名思义就是供给数据给调用环境,不接收参数传递

   * T get() : 返回泛型T类型的参数到调用环境

   public static void main(String[] args) {

   // 返回一个0-100间的随机数

   Supplier Integer sup1 = new Supplier Integer () {

   @Override

   public Integer get() {

   int res = new Random().nextInt(100);

   System.out.println("通过匿名内部类的方式获取到的随机数:"+ res);

   return res;

   // 执行该方法的时候,它会去执行我们上述实现的get方法。

   sup1.get();

   // 通过lambda表达式的方式进行实现

   Supplier Integer sup2 = ()- {

   int res = new Random().nextInt(100);

   System.out.println("通过lambda表达式的方式获取到的随机数:"+ res);

   return res;};

   sup2.get();

   * 最终结果:

   * 通过匿名内部类的方式获取到的随机数:28

   * 通过lambda表达式的方式获取到的随机数:62

   * 使用lambda表达式,我们只需要记住参数列表和执行逻辑即可,其他的我们无需关注。

  }

 

  学习案例

  

public class Main{

 

   public static Map String,String redis = new HashMap();

   * 供给型函数式接口顾名思义就是顾名思义就是供给数据给调用环境,不接收参数传递

   * T get() : 返回泛型T类型的参数到调用环境

   public static void main(String[] args) {

   // 1.(模拟)查询某个Key在redis中有没有缓存,缓存没有则从数据库取完存入redis再返回,有的话则直接返回

   String val = getCache("title");

   String val2 = getCache("title");

   String val3 = getCache("title");

   * 最终结果:

   从数据库中获取:我是标题

   从缓存中获取:我是标题

   从缓存中获取:我是标题

   // 可以看到经过第一次后续都是直接从缓存中取出的数据

   public static String getCache(String key){

   String val = redis.get(key);

   if(Objects.isNull(val)){

   // 获取数据库的数据

   val = getDbVal(() - "我是标题");

   System.out.println("从数据库中获取:"+val);

   redis.put(key,val);

   return val;

   System.out.println("从缓存中获取:"+val);

   return val;

   public static String getDbVal(Supplier String supplier){

   return supplier.get();

  }

 

  1.函数式接口的本质实际上就是将函数以参数的形式进行传递

  2.Supplier是供给型的函数式接口,通常用于构建某个对象处理后返回调用环境

  3.除了Supplier之外,还有各种供给型的函数式接口,还有BooleanSupplier、IntSupplier等。

  Function T,R

  Function T,R 函数型的函数式接口,泛型T是参数、泛型R则是返回值、主要应用场景做数据类型转换等。

  

public class Main{

 

   * Function T,R 函数型的函数式接口,泛型T是参数、泛型R则是返回值、主要应用场景做数据类型转换等。

   * R apply(T t): 抽象方法实现,用于调用方法并返回泛型R

   * V Function T, V andThen: 默认实现方法,内部允许我们链式调用,与其他的andThen原理一致。

   * V Function V, R compose: 默认实现方法,内部允许我们链式调用,调用方式与andThen一样,但执行顺序不一样,compose是先执行compose中的函数接口,再执行左边调用的函数接口,依次往左

   * T Function T, T identity():返回当前执行的方法,从源码中我们也可以看到它返回的是当前的t

   public static void main(String[] args) {

   // 传入给定字符串,返回转换后的Integer类型

   Function String,Integer fun1 = new Function String, Integer () {

   @Override

   public Integer apply(String s) {

   Integer convert = Integer.valueOf(s);

   System.out.println("通过匿名内部类的方式获取到的值:"+ convert +",数据类型是否为Integer?结果:" + (convert instanceof Integer));

   return convert;

   // 执行该方法的时候,它会去执行我们上述实现的apply方法。

   fun1.apply("10086");

   // 通过lambda表达式的方式进行实现

   Function String,Integer fun2 = s- {

   Integer convert = Integer.valueOf(s);

   System.out.println("通过lambda表达式的方式获取到的值:"+ convert +",数据类型是否为Integer?结果:" + (convert instanceof Integer));

   return convert;

   fun2.apply("10000");

   * 通过匿名内部类的方式获取到的值:10086,数据类型是否为Integer?结果:true

   * 通过lambda表达式的方式获取到的值:10000,数据类型是否为Integer?结果:true

   * 使用lambda表达式,我们只需要记住参数列表和执行逻辑即可,其他的我们无需关注。

   // 我们继续对Function的API做一些理解和补充,毕竟这玩意在工作中经常会用上

   // andThen 我们都知道常用于链式调用的,这里必须保证T和V类型是一样的,也就是参数泛型T和返回值泛型V

   Function String,String fun3 = x- {

   System.out.println("我是fun3的方法");

   return x;

   Function String,String fun4 = y- {

   System.out.println("我是fun4的方法");

   return y;

   fun3.andThen(fun4).apply("test");

   * 最终结果:

   * 我是fun3的方法

   * 我是fun4的方法

   // 我们发现这里是先执行fun3的apply方法再执行fun4的apply方法的。

   // compose 与andThen一样都是链式调用,但结果却大大不同,这里必须保证T和V类型是一样的,也就是参数泛型T和返回值泛型V

   fun3.compose(fun4).apply("test");

   * 最终结果:

   * 我是fun4的方法

   * 我是fun3的方法

   // 我们发现这里是先执行的fun4的apply方法再执行fun3的apply方法的

   // 由此我们推断出compose和andThen的区别就在于,compose接口方法执行顺序从右到左,而andThen则是从左到右。

   Function Object, Object identity = Function.identity();

   // Function.identity() 静态方法这里就不好演示了,这个通常在后面搭配Stream流转Map类型的时候用到,它返回本身

  }

 

  

public class Main{

 

   * Function T,R 函数型的函数式接口,泛型T是参数、泛型R则是返回值、主要应用场景做数据类型转换等。

   * R apply(T t): 抽象方法实现,用于调用方法并返回泛型R

   * V Function T, V andThen: 默认实现方法,内部允许我们链式调用,与其他的andThen原理一致。

   * V Function V, R compose: 默认实现方法,内部允许我们链式调用,调用方式与andThen一样,但执行顺序不一样,compose是先执行compose中的函数接口,再执行左边调用的函数接口,依次往左

   * T Function T, T identity():返回当前执行的方法,从源码中我们也可以看到它返回的是当前的t

   public static void main(String[] args) {

   List Person persons = Arrays.asList(new Person(1,"张三"),new Person(2,"李四"));

   // 给定一个person对象集、转换成姓名属性集合返回

   Function List Person ,List String fun1 = list- {

   List String arr = new ArrayList ();

   for (int i = 0; i list.size(); i++) {

   arr.add(list.get(i).getName());

   return arr;

   List String personNames = fun1.apply(persons);

   System.out.println(personNames);

   * 最终结果:

   * 结果:[张三, 李四]

  class Person{

   private Integer id;

   private String name;

   public Person(Integer id, String name) {

   this.id = id;

   this.name = name;

  
1.函数式接口的本质实际上就是将函数以参数的形式进行传递

  2.Function是函数型的函数式接口,通常用于构建某个对象处理后返回调用环境

  3.除了Function之外,还有各种函数型的函数式接口,还有BIFunction、ToIntFunction等。

  Predicate

  Predicate T 断言型的函数式接口,泛型T是参数、返回结果类型为布尔类型的函数接口。

  

public class Main{

 

   * Predicate T 断言型的函数式接口,泛型T是参数、返回结果类型为布尔类型的函数接口。

   * boolean test(T t): 抽象方法实现,用于返回传入的参数逻辑运算后布尔类型结果

   * Predicate T and: 默认实现方法,内部允许我们链式调用,用于同时判定多个Predicate函数接口的实现 类似于逻辑运算中的短路 操作。

   * Predicate T negate: 默认实现方法,内部允许我们链式调用,用于同时判定多个Predicate函数接口的实现,用于将当前判定结果取反后返回,类似于逻辑运算中的!操作

   * Predicate T or:默认实现方法,内部允许我们链式调用,用于同时判定多个Predicate函数接口的实现 类似于逻辑运算中的操作。

   * Predicate T isEqual:静态方法,内部允许我们链式调用,在保证参数不是空的情况下它内部实现逻辑实际上调用的是Object的equals,具体equals看子类有没有重写

   public static void main(String[] args) {

   Predicate String pre1 = new Predicate String () {

   @Override

   public boolean test(String o) {

   boolean bool = o.matches("[0-9]{1,}");

   System.out.println("通过匿名内部类的方式获取到的值:"+ bool);

   return bool;

   // 执行该方法的时候,它会去执行我们上述实现的test方法。

   pre1.test("10086");

   Predicate String pre2 = text- {

   boolean bool = text.matches("[0-9]{1,}");

   System.out.println("通过lambda表达式的方式获取到的值:"+ bool);

   return bool;

   pre2.test("10086a");

   * 最终结果:

   * 通过匿名内部类的方式获取到的值:true

   * 通过lambda表达式的方式获取到的值:false

   * 使用lambda表达式,我们只需要记住参数列表和执行逻辑即可,其他的我们无需关注。

   // 我们继续对Predicate的API做一些理解和补充,毕竟这玩意在工作中经常会用上

   // and 实际上等价于逻辑运算符中的短路 操作

   Predicate String fun3 = x-

   System.out.println("先计算fun3");

   return true;

   Predicate String fun4 = x-

   System.out.println("先计算fun4");

   return false;

   System.out.println("第一次and结果:"+fun3.and(fun4).test("test"));

   * 最终结果:

   * 先计算fun3

   * 先计算fun4

   * 本次结果:false

   // 那么为什么我们知道它是短路 的操作 而不是 的操作呢?,我们只需要将第一个函数式接口返回false,看看它还会不会执行第二个函数式接口即可

   Predicate String fun5 = x-

   System.out.println("先计算fun5");

   return false;

   Predicate String fun6 = x-

   System.out.println("先计算fun6");

   return true;

   System.out.println("第二次and结果:"+fun5.and(fun6).test("test"));

   * 最终结果:

   * 先计算fun5

   * 第二次and结果:false。

   // 从结果我们其实可以推断出,在第一个结果为true的情况下第二个fun6压根没进,所以是短路

   // 并且起始在and方法源码中给我们也可以看到 return (t) - test(t) other.test(t); 是短路

   // negate 实际上等价于逻辑运算符中的!操作

   // 我们直接取上面的值做例子,本来结果应该为false,取反后应该为true

   System.out.println("negate结果:"+fun5.and(fun6).negate().test("test"));

   * 最终结果:

   * negate结果:true

   // or 等价于逻辑运算符中的操作

   // 我们直接取上面的做例子,第一个为false,第二个为true、的最终结果应该为true

   System.out.println("or结果:"+fun5.or(fun6).test("test"));

   * 最终结果:

   * or结果:true

   // isEqual 内部调用的是Object的equals方法,如果子类重写了equals则调起子类的equals方法

   // 如我们常用的String就重写了Object的equals方法,我们以它做例子

   Predicate String fun7 = Predicate.isEqual("Hello");

   System.out.println("isEquals第一次结果:"+ fun7.test("Hello"));

   * 最终结果:

   * isEquals第一次结果:true

   Predicate String fun8 = Predicate.isEqual("World");

   System.out.println("isEquals第二次结果:"+ fun8.test("Hello"));

   // 自定义的对象类型也是可以比较的,但需要重写equals和hashCode,这里就不写示例了,可以自己玩玩

   // 以上就是Predicate的相关API的介绍

  }

 

  

public class Main{

 

   * Predicate T 断言型的函数式接口,泛型T是参数、返回结果类型为布尔类型的函数接口。

   * boolean test(T t): 抽象方法实现,用于返回传入的参数逻辑运算后布尔类型结果

   * Predicate T and: 默认实现方法,内部允许我们链式调用,用于同时判定多个Predicate函数接口的实现 类似于逻辑运算中的短路 操作。

   * Predicate T negate: 默认实现方法,内部允许我们链式调用,用于同时判定多个Predicate函数接口的实现,用于将当前判定结果取反后返回,类似于逻辑运算中的!操作

   * Predicate T or:默认实现方法,内部允许我们链式调用,用于同时判定多个Predicate函数接口的实现 类似于逻辑运算中的操作。

   * Predicate T isEqual:静态方法,内部允许我们链式调用,在保证参数不是空的情况下它内部实现逻辑实际上调用的是Object的equals,具体equals看子类有没有重写

   public static void main(String[] args) {

   // 判断给定字符串是否纯数字并且小于10 可以使用and进行链式调用

   Predicate String pre1 = ((Predicate String ) s - s.matches("[0-9]{1,}")).and(x- Integer.valueOf(x)

   System.out.println("使用and方式进行链式调用:"+pre1.test("9"));

   // 需要注意的是:要使用这种方式,第一个Predicate要进行链式调用必须要强行再指定为(Predicate)类型,后续的接口才能够调用方法

   // 实际上这种方式用的比较少,因为比较麻烦,所以一般都会直接使用 进行判定

   Predicate String pre2 = s- s.matches("[0-9]{1,}") Integer.valueOf(s)

   System.out.println("使用 方式调用:"+pre2.test("10"));

   * 最终结果:

   * 使用and方式进行链式调用:true

   * 使用 方式调用:false

  }

 

  
以上就是Jdk8提供的基础的四大函数(除了这四个之外,还有许多函数式接口,当然我们也可以自定义函数式接口)的基本使用方式和一些简单案例,具体该怎么做怎么写则需要根据项目实际需求进行,通常函数式接口都会搭配Stream成套使用,目前也有很多框架支持函数式接口的方式、如MyBatis-plus等社区活跃度较高的框架。

  

  以上就是【java8新特性】02:常见的函数式接口()的详细内容,想要了解更多 【java8新特性】02:常见的函数式接口的内容,请持续关注盛行IT软件开发工作室。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: