Java函数式接口实现解析

我特别提出这个问题,是因为我被某一份源码深深震撼了。

我想研究这个令人震撼的实现,并模仿它,然后将常规的实现贴出来,让读者能够看到相对完整的实现。

值得一提的是,本文的代码是基于JDK17。

一、令人震撼的代码

我要谈一下Collectors.toList()

我们来看一下CollectorImpl的构造器:

第二个参数是BiConsumer<A, T>,再来看一下BiConsumer的接口方法:

按照正常的逻辑,当toList()调用CollectorImpl的时候,应该传递一个具有两个参数的方法。但问题是List.add只有一个参数。

果然,List.add有两个实现:

很明显,它不可能指向一个具有两个参数的实现,因为参数类型明显不匹配,只能指向boolean add(E e)。

但实际上,add(E e)看起来也不匹配,因为它只有一个参数。

事实上,编译器并不会报错,而且能够得到正确的结果。

为什么会这样呢?

仔细思考后,我只能说 JCP(Java社区流程)改变了规则,允许具有独特实现来实现目的。

以往的源码中,我们看到的好像都要求参数个数和类型匹配?

二、我的模仿和可能的解释

为了验证这种独特的函数式接口实现,我做了一个测试,在测试代码中:

1. 创建一个类似ArrayList的类

2. 写了一段测试代码,验证这个奇特的实现

具体代码如下:

测试后,输出的结果如下图:

根据java的例子和我自己的编写例子,我只能得出这样的某种猜测:

如果函数式接口方法F要求两个参数(R, T),那么当引用对象方法(假定对象称为 Test,方法是 shockMe) 实现函数式接口的时候,允许引用这样的接口:

1. Test.ShockMe可以有一个参数,类型为T,ShockMe的方法返回类型同F的返回类型,或者都是Void.class

2. Test本身是R类型

那么JCP认为这是合规的。

根据这种推测,那么可能也允许:F有n个参数,但是ShockMe有n-1个参数的情况。暂时未验证。

JCP为什么要允许这种的实现可行了?大概是为了向后兼容,不想浪费已有的各种实现。

我们反过来想,如果不允许这样,那么JAVA应该怎么办?

以toList()为例,那么就必须增加一个实现方法,或者额外写几个工具类。JCP不知道出于什么考虑,想出了这个比较其它的实现。

虽然这种实现有其好处: 向后兼容,不浪费。但也造成代码不容易看懂(是的,我迷惑了很久)

不知道其它语言是否有类似的情况?

三、函数式接口标准的五个实现

以下代码,在我的其他文章也有: JAVA基础之四-郎打表达式、函数式接口、流的简介

为了方便,重复一次

标签:游戏攻略