首页 > 编程语言 >Function源码解析与实践

Function源码解析与实践

时间:2022-11-29 11:31:07浏览次数:43  
标签:Function String System 源码 println 解析 public out

作者:陈昌浩


1 导读

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


2 Function 接口

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

 

@FunctionalInterface
interface TestFunctionService
{
void addHttp(String url);
}

 

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

 

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

 

2.1 FunctionalInterface

2.1.1 源码

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}

 

2.1.2 说明

Function源码解析与实践_函数式接口

 

上图是 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 源码

@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}

 

3.1.2 方法说明

  • apply:抽象方法。将此函数应用于给定的参数。参数 t 通过具体的实现返回 R。
  • compose:default 方法。返回一个复合函数,首先执行 fefore 函数应用于输入,然后将该函数应用于结果。如果任意一个函数的求值引发异常,则将其传递给组合函数的调用者。
  • andThen:default 方法。返回一个复合函数,该复合函数首先对其应用此函数它的输入,然后对结果应用 after 函数。如果任意一个函数的求值引发异常,则将其传递给组合函数的调用者。
  • identity:static 方法。返回一个始终返回其输入参数的函数。

3.1.3 方法举例

1)apply

测试代码:

 

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

 

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

Function源码解析与实践_代码简化_02

2)compose

测试代码:

 

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

 

执行结果

Function源码解析与实践_Java_03

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

3)andThen

测试代码:

 

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

 

执行结果:

Function源码解析与实践_Function_04

 

如结果所示:

andThen 先执行 function1 后执行 function2。

  • identity

测试代码:

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

 

执行结果:

Function源码解析与实践_Java_05


3.2 Supplier

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

3.2.1 源码

@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}

 

3.2.2 方法说明

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

3.2.3 方法举例

public class SupplierTest {
SupplierTest(){
System.out.println(Math.random());
System.out.println(this.toString());
}
}
public static void main(String[] args) {
Supplier<SupplierTest> sup = SupplierTest::new;
System.out.println("调用一次");
sup.get();
System.out.println("调用二次");
sup.get();
}

 

执行结果:

Function源码解析与实践_抽象方法_06

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


3.3 Consumer

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

3.3.1 源码

@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}

 

3.3.2 方法说明

  • accept:对给定参数 T 执行一些操作。
  • andThen:按顺序执行 Consumer -> after , 如果执行操作引发异常,该异常被传递给调用者。

3.3.3 方法举例

 

public static void main(String[] args) {
Consumer<String> consumer = s -> System.out.println("consumer_"+s);
Consumer<String> after = s -> System.out.println("after_"+s);
consumer.accept("isReady");
System.out.println("========================");
consumer.andThen(after).accept("is coming");
}

 

执行结果:

Function源码解析与实践_函数式接口_07

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

3.4 Runnable

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

3.4.1 源码

@FunctionalInterface
public interface Runnable {
public abstract void run();
}

 

3.4.2 方法说明

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

3.4.3 方法举例

public class TestRun implements Runnable {
@Override
public void run() {
System.out.println("TestRun is running!");
}
}
public static void main(String[] args) {
Thread thread = new Thread(new TestRun());
thread.start();
}

 

执行结果:

Function源码解析与实践_Function_08

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


4 Function 接口用法

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

4.1 差异处理

原代码:

@Data
public class User {
/**
* 姓名
*/
private String name;
/**
* 年龄
*/
private int age;
/**
* 组员
*/
private List<User> parters;
}
public static void main(String[] args) {
User user =new User();
if(user ==null ||user.getAge() <18 ){
throw new RuntimeException("未成年!");
}
}

 执行结果:

Function源码解析与实践_抽象方法_09

 

使用 Function 接口后的代码:

@FunctionalInterface
public interface testFunctionInfe {
/**
* 输入异常信息
* @param message
*/
void showExceptionMessage(String message);
}
public static testFunctionInfe doException(boolean flag){
return (message -> {
if (flag){
throw new RuntimeException(message);
}
});
}
public static void main(String[] args) {
User user =new User();
doException(user ==null ||user.getAge() <18).showExceptionMessage("未成年!");
}

 

执行结果:

Function源码解析与实践_Function_10

 

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


4.2 处理 if…else…

原代码:

public static void main(String[] args) {
User user =new User();
if(user==null){
System.out.println("新增用户");
}else {
System.out.println("更新用户");
}
}

 

使用 Function 接口后的代码:

public static void main(String[] args) {
User user =new User();
Consumer trueConsumer = o -> {
System.out.println("新增用户");
};
Consumer falseCnotallow= o -> {
System.out.println("更新用户");
};
trueOrFalseMethdo(user).showExceptionMessage(trueConsumer,falseConsumer);
}
public static testFunctionInfe trueOrFalseMethdo(User user){
return ((trueConsumer, falseConsumer) -> {
if(user==null){
trueConsumer.accept(user);
}else {
falseConsumer.accept(user);
}
});
}
@FunctionalInterface
public interface testFunctionInfe {
/**
* 不同分处理不同的事情
* @param trueConsumer
* @param falseConsumer
*/
void showExceptionMessage(Consumer trueConsumer,Consumer falseConsumer);
}

 

执行结果:

Function源码解析与实践_函数式接口_11


4.3 处理多个 if

原代码:

public static void main(String[] args) {
String flag="";
if("A".equals(flag)){
System.out.println("我是A");
}else if ("B".equals(flag)) {
System.out.println("我是B");
}else if ("C".equals(flag)) {
System.out.println("我是C");
}else {
System.out.println("没有对应的指令");
}
}

 

使用 Function 接口后的代码:

public static void main(String[] args) {
String flag="B";
Map<String, Runnable> map =initFunctionMap();
trueOrFalseMethdo(map.get(flag)==null).showExceptionMessage(()->{
System.out.println("没有相应指令");
},map.get(flag));
}
public static Map<String, Runnable> initFunctionMap(){
Map<String,Runnable> result = Maps.newHashMap();
result.put("A",()->{System.out.println("我是A");});
result.put("B",()->{System.out.println("我是B");});
result.put("C",()->{System.out.println("我是C");});
return result;
}
public static testFunctionInfe trueOrFalseMethdo(boolean flag){
return ((runnable, falseConsumer) -> {
if(flag){
runnable.run();
}else {
falseConsumer.run();
}
});
}

 

执行结果:

Function源码解析与实践_Java_12


5 总结

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

标签:Function,String,System,源码,println,解析,public,out
From: https://blog.51cto.com/u_15714439/5894627

相关文章

  • pinia源码解读三(创建store)
    定义模块这一步很复杂,用几个流程图来梳理一下createOptionsStore方法createSetupStore方法数据流向最后整理下数据的流向变更,对于写pinia插件很有帮助option写法版......
  • app直播源码,flutter Text自动计算文本内容的宽度
    app直播源码,flutterText自动计算文本内容的宽度一、什么是TextPainter在内容开始之前,我们先来看一下它的属性 TextPainter({  InlineSpan?text,   //Tex......
  • easylogging++的那些事(四)源码分析(二)日志记录宏(四)偶尔日志宏
    目录CLOG_EVERY_N宏宏展开Info日志宏CLOG_EVERY_N(xxx,INFO,xxx)Trace日志宏CLOG_EVERY_N(xxx,TRACE,xxx)Debug日志宏CLOG_EVERY_N(xxx,DEBUG,xxx)Fatal日志......
  • LevelDB源码剖析(3) Skiplist跳表
    1.背景什么是跳表?跳表是SortedMap的一种具体实现,是一种概率性的数据结构。跳表拥有SortedMap的所有功能,定位和红黑树类似,其和红黑树的区别在于优点:跳表的实现更加简单......
  • 深入理解Kubernetes 4A - Audit源码解析
    Overview本文是关于Kubernetes4A解析的第四章深入理解Kubernetes4A-Authentication源码解析深入理解Kubernetes4A-Authorization源码解析深入理解Kubernetes......
  • Swift 2023:强调并发、泛型和 C++ 互操作性,开发 Swift 解析器
    AppleSwift团队的一名工程师兼语言工作组成员JohnMcCall在最新发布的一篇博客中介绍了Swift的2023年度计划。“Swift项目中有很多激动人心的工作正在进行,而且很......
  • cocos2d-x 是男人就下100层 附源码
    1.效果图:  玩法:一个不断下降的小人,点击屏幕的left或者right控制小人的移动方向,尽可能生存久些. 为什么要搞这个游戏呢?因为在2012年的8月份,我完成它的android版本,......
  • C++学习------cmath头文件的源码学习06
    函数族定义---双曲函数cosh---计算双曲余弦函数sinh---计算双曲正弦函数tanh---计算双曲正切函数acosh---计算双曲余弦面积asinh---计算双曲正弦面积atanh---计算双曲正切面......
  • 0130-Go-数值解析
    环境Time2022-08-25Go1.19前言说明参考:https://gobyexample.com/number-parsing目标使用Go语言的数值解析。示例packagemainimport("fmt""st......
  • 0131-Go-URL 解析
    环境Time2022-08-25Go1.19前言说明参考:https://gobyexample.com/url-parsing目标使用Go语言的URL解析。示例packagemainimport("fmt""net"......