Function源码解析与实践

作者:陈昌浩

1 导读

if…else…在代码中经常使用,听说可以通过Java 8的Function接口来消灭if…else…!Function接口是什么?如果通过Function接口接口消灭if…else…呢?让我们一起来探索一下吧。

2 Function接口

Function接口就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口,Function接口可以被隐式转换为 lambda 表达式。可以通过FunctionalInterface注解来校验Function接口的正确性。Java 8允许在接口中加入具体方法。接口中的具体方法有两种,default方法和static方法。

1@FunctionalInterface 2interface TestFunctionService 3{ 4 void addHttp(String url); 5}

那么就可以使用Lambda表达式来表示该接口的一个实现。

TestFunctionService testFunctionService = url -> System.out.println("http:" + url);

2.1 FunctionalInterface

2.1.1 源码
1@Documented 2@Retention(RetentionPolicy.RUNTIME) 3@Target(ElementType.TYPE) 4public @interface FunctionalInterface {}
2.1.2 说明

上图是FunctionalInterface的注解说明。通过上面的注解说明,可以知道FunctionalInterface是一个注解,用来说明一个接口是函数式接口。 函数式接口只有一个抽象方法。 可以有默认方法,因为默认方法有一个实现,所以不是抽象的。函数接口的实例可以用lambda表达式、方法引用或构造函数引用创建。

FunctionalInterface会校验接口是否满足函数式接口:

  • 类型必须是接口类型,不能是注释类型、枚举或类。
  • 只能有一个抽象方法。
  • 可以有多个默认方法和静态方法。
  • 可以显示覆盖java.lang.Object中的抽象方法。

编译器会将满足函数式接口定义的任何接口视为函数式接口,而不管该接口声明中是否使用FunctionalInterface注解。

3 Function接口主要分类

Function接口主要分类:

  • Function:Function函数的表现形式为接收一个参数,并返回一个值。
  • Supplier:Supplier的表现形式为不接受参数、只返回数据。
  • Consumer:Consumer接收一个参数,没有返回值。
  • Runnable:Runnable的表现形式为即没有参数也没有返回值。

3.1 Function

Function函数的表现形式为接收一个参数,并返回一个值。

3.1.1 源码
1@FunctionalInterface 2public interface Function<T, R> { 3 R apply(T t); 4 default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { 5 Objects.requireNonNull(before); 6 return (V v) -> apply(before.apply(v)); 7 } 8 default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { 9 Objects.requireNonNull(after); 10 return (T t) -> after.apply(apply(t)); 11 } 12 static <T> Function<T, T> identity() { 13 return t -> t; 14 } 15}
3.1.2 方法说明
  • apply:抽象方法。将此函数应用于给定的参数。参数t通过具体的实现返回R。
  • compose:default方法。返回一个复合函数,首先执行fefore函数应用于输入,然后将该函数应用于结果。如果任意一个函数的求值引发异常,则将其传递给组合函数的调用者。
  • andThen:default方法。返回一个复合函数,该复合函数首先对其应用此函数它的输入,然后对结果应用after函数。如果任意一个函数的求值引发异常,则将其传递给组合函数的调用者。
  • identity:static方法。返回一个始终返回其输入参数的函数。
3.1.3 方法举例

1)apply

测试代码:

1public String upString(String str){ 2 Function<String, String> function1 = s -> s.toUpperCase(); 3 return function1.apply(str); 4} 5 public static void main(String[] args) { 6 System.out.println(upString("hello!")); 7 }

通过apply调用具体的实现。执行结果:

2)compose

测试代码:

1public static void main(String[] args) { 2 Function<String, String> function1 = s -> s.toUpperCase(); 3 Function<String, String> function2 = s -> "my name is "+s; 4 String result = function1.compose(function2).apply("zhangSan"); 5 System.out.println(result); 6}

执行结果

如结果所示:compose 先执行function2 后执行function1。

3)andThen

测试代码:

1public static void main(String[] args) { 2 Function<String, String> function1 = s -> s.toUpperCase(); 3 Function<String, String> function2 = s -> "my name is "+s; 4 String result = function1.andThen(function2).apply("zhangSan"); 5 System.out.println(result); 6}

执行结果:

如结果所示:

andThen先执行function1 后执行function2。

  • identity

测试代码:

1public static void main(String[] args) { 2 Stream<String> stream = Stream.of("order", "good", "lab", "warehouse"); 3 Map<String, Integer> map = stream.collect(Collectors.toMap(Function.identity(), String::length)); 4 System.out.println(map); 5}

执行结果:

3.2 Supplier

Supplier的表现形式为不接受参数、只返回数据。

3.2.1 源码
1@FunctionalInterface 2public interface Supplier<T> { 3 /** 4 * Gets a result. 5 * 6 * @return a result 7 */ 8 T get(); 9}
3.2.2 方法说明

get:抽象方法。通过实现返回T。

3.2.3 方法举例
1public class SupplierTest { 2 SupplierTest(){ 3 System.out.println(Math.random()); 4 System.out.println(this.toString()); 5 } 6} 7 public static void main(String[] args) { 8 Supplier<SupplierTest> sup = SupplierTest::new; 9 System.out.println("调用一次"); 10 sup.get(); 11 System.out.println("调用二次"); 12 sup.get(); 13}

执行结果:

如结果所示:Supplier建立时并没有创建新类,每次调用get返回的值不是同一个。

3.3 Consumer

Consumer接收一个参数,没有返回值。

3.3.1 源码
1@FunctionalInterface 2public interface Consumer<T> { 3 void accept(T t); 4 default Consumer<T> andThen(Consumer<? super T> after) { 5 Objects.requireNonNull(after); 6 return (T t) -> { accept(t); after.accept(t); }; 7 } 8}
3.3.2 方法说明
  • accept:对给定参数T执行一些操作。
  • andThen:按顺序执行Consumer -> after ,如果执行操作引发异常,该异常被传递给调用者。
3.3.3 方法举例
1public static void main(String[] args) { 2 Consumer<String> consumer = s -> System.out.println("consumer_"+s); 3 Consumer<String> after = s -> System.out.println("after_"+s); 4 consumer.accept("isReady"); 5 System.out.println("========================"); 6 consumer.andThen(after).accept("is coming"); 7}

执行结果:

如结果所示:对同一个参数T,通过andThen 方法,先执行consumer,再执行fater。

3.4 Runnable

Runnable:Runnable的表现形式为即没有参数也没有返回值。

3.4.1 源码
1@FunctionalInterface 2public interface Runnable { 3 public abstract void run(); 4}
3.4.2 方法说明

run:抽象方法。run方法实现具体的内容,需要将Runnale放入到Thread中,通过Thread类中的start()方法启动线程,执行run中的内容。

3.4.3 方法举例
1public class TestRun implements Runnable { 2 @Override 3 public void run() { 4 System.out.println("TestRun is running!"); 5 } 6} 7 public static void main(String[] args) { 8 Thread thread = new Thread(new TestRun()); 9 thread.start(); 10 }

执行结果:

如结果所示:当线程实行start方法时,执行Runnable 的run方法中的内容。

4 Function接口用法

Function的主要用途是可以通过lambda 表达式实现方法的内容。

4.1 差异处理

原代码:

1@Data 2public class User { 3 /** 4 * 姓名 5 */ 6 private String name; 7 /** 8 * 年龄 9 */ 10 private int age; 11 /** 12 * 组员 13 */ 14 private List<User> parters; 15} 16 public static void main(String[] args) { 17 User user =new User(); 18 if(user ==null ||user.getAge() <18 ){ 19 throw new RuntimeException("未成年!"); 20 } 21}

执行结果:

使用Function接口后的代码:

1@FunctionalInterface 2public interface testFunctionInfe { 3 /** 4 * 输入异常信息 5 * @param message 6 */ 7 void showExceptionMessage(String message); 8} 9 public static testFunctionInfe doException(boolean flag){ 10 return (message -> { 11 if (flag){ 12 throw new RuntimeException(message); 13 } 14 }); 15 } 16 public static void main(String[] args) { 17 User user =new User(); 18 doException(user ==null ||user.getAge() <18).showExceptionMessage("未成年!"); 19}

执行结果:

使用function接口前后都抛出了指定的异常信息。

4.2 处理if…else…

原代码:

1public static void main(String[] args) { 2 User user =new User(); 3 if(user==null){ 4 System.out.println("新增用户"); 5 }else { 6 System.out.println("更新用户"); 7 } 8}

使用Function接口后的代码:

1public static void main(String[] args) { 2 User user =new User(); 3 Consumer trueConsumer = o -> { 4 System.out.println("新增用户"); 5 }; 6 Consumer falseConsumer= o -> { 7 System.out.println("更新用户"); 8 }; 9 trueOrFalseMethdo(user).showExceptionMessage(trueConsumer,falseConsumer); 10} 11public static testFunctionInfe trueOrFalseMethdo(User user){ 12 return ((trueConsumer, falseConsumer) -> { 13 if(user==null){ 14 trueConsumer.accept(user); 15 }else { 16 falseConsumer.accept(user); 17 } 18 }); 19} 20@FunctionalInterface 21public interface testFunctionInfe { 22 /** 23 * 不同分处理不同的事情 24 * @param trueConsumer 25 * @param falseConsumer 26 */ 27 void showExceptionMessage(Consumer trueConsumer,Consumer falseConsumer); 28}

执行结果:

4.3 处理多个if

原代码:

1public static void main(String[] args) { 2 String flag=""; 3 if("A".equals(flag)){ 4 System.out.println("我是A"); 5 }else if ("B".equals(flag)) { 6 System.out.println("我是B"); 7 }else if ("C".equals(flag)) { 8 System.out.println("我是C"); 9 }else { 10 System.out.println("没有对应的指令"); 11 } 12}

使用Function接口后的代码:

1public static void main(String[] args) { 2 String flag="B"; 3 Map<String, Runnable> map =initFunctionMap(); 4 trueOrFalseMethdo(map.get(flag)==null).showExceptionMessage(()->{ 5 System.out.println("没有相应指令"); 6 },map.get(flag)); 7} 8public static Map<String, Runnable> initFunctionMap(){ 9 Map<String,Runnable> result = Maps.newHashMap(); 10 result.put("A",()->{System.out.println("我是A");}); 11 result.put("B",()->{System.out.println("我是B");}); 12 result.put("C",()->{System.out.println("我是C");}); 13 return result; 14} 15public static testFunctionInfe trueOrFalseMethdo(boolean flag){ 16 return ((runnable, falseConsumer) -> { 17 if(flag){ 18 runnable.run(); 19 }else { 20 falseConsumer.run(); 21 } 22 }); 23}

执行结果:

5 总结

Function函数式接口是java 8新加入的特性,可以和lambda表达式完美结合,是非常重要的特性,可以极大的简化代码。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

FLV文件格式

1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  

mysql设置时区

mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0