2

大家有没有发现java.util.function包(本文后面简称function包)下的接口虽然比较多(目前共43个),但都是定义很简单的(方法很少),而我们理解起来却十分的困难。其实啊,这也不怪大家,是因为这个包下的接口并不是典型的Java的API,你可以说它们是“异类”。那么问题来了,典型的Java API是什么样子的呢?function包下的接口又“异”在哪里?

总所周知,Java是一种典型的OOP面向对象编程)语言,封装、继承、多态自不必多说,大家想必都十分清楚了。但这里为了解释上面的问题,彬哥还是得多啰嗦几句。面向对象一个很重要的标识就是,把结构和行为封装到一个类(对象)中。

典型的Java API就是这样子的,举个大家都很熟悉的例子:java.util.List。List是一个接口,是接口就只有方法,没有属性。但是,咱们来看看的List中的方法:size()、isEmpty()、contains(Object o)等等,按照正常人的思维,很容易、也很自然会将这些方法理解成:“假设我是个List的对象(实例)话,你们调用我的size()方法,我会将我的size告诉你;调用我的isEmpty()方法,我会告诉你我是不是空的;调用我的contains()方法,我会告诉你我是不是包含了你指定的这个对象”。大家有没有发现点什么?所有这些方法,很自然的加入到“我”这个语境(context)中去了。

那么再来看看这个咱们这个专题的主角function包看,找个典型代表出来:java.util.function.Consumer<T>Consumer是个Functional接口,有且只有一个“抽象方法”--void accept(T t)。这个accept方法谁能告诉我这™是什么鬼?彬哥现在也不知道,这还真不是彬哥故意卖关子。这是因为啊,你可以function包下的接口都是“无意义的”,这个“无意义”并不是说它们没有存在的意义,而是说它们都必须放到具体的语境中去才会真正的意义。或者说,它们是“非典型”的Java API就体现在,它们自己是没有语境(Context)的,它们实际上可以说就是为Lambda表达式而存在的--你不需要了解他叫什么(匿名的),它们是作为参数在对象的方法的中传递的。

它们没有自己的结构,只定义行为,这很不OOP,但很FP(Functional Programming),所以你可以说,从JDK 8开始,函数也可以被称作Java世界中的“第一公民”了。

来看个具体的例子,java.lang.IterableforEach方法:

default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        action.accept(t);
    }
}

它的参数就是个Consumer,从forEach这个方法的定义来看,现在我们可以理解到“遍历我所包含的所有元素,对每个元素都执行一次action.accept()”,还有点抽象?不过已经有些好理解了是吧?咱们再进一步看:

List<String> list = new ArrayList<>();
list.add("Hello ");
list.add("彬哥!");
list.forEach(s -> System.out.println(s)); // 这里当然也可以用更为简洁的方法引用来改写

现在在具体的语境里来理解就很容易了,“对于list中的每一个元素,执行我传进去的那个函数--Consumer”。可能咱们每天都在用类似的写法,但可能从来都没有意识到,那原来就是个Consumer啊。这不怪大家,因为“它们都是无意义的”,就像在这里,你根本不需要知道我们其实最后是调用的了Consumeraccept方法。
其实啊,所有java.util.function包下的接口的方法名,我们都不必关心,因为当我们用它们的时候,压根不会去显式地调用它们,方法名也是“无意义”的,它们只是方便大家去理解他的用途。但是,方法的参数和返回值是需要我们关心和注意的。

function包下总共有43个接口,嗯~看上去数量还是不少。不必害怕,其实满打满算也就这么5类:
FunctionSupplierConsumerPredicateOperator。这么看就很少了吧。这还不只,它们还有很多相似的地方,咱们再根据这些相似点分类、举一反三,就会简单很多。这里先按照这些共性的点,给这43个接口细分下归类:

  • Function类

    • 一元:Function

      • 原始类型

        • 作为参数:DoubleFunctionIntFunctionLongFunction
        • 作为返回值:ToDoubleFunctionToIntFunctionToLongFunction
        • 相互转化:DoubleToIntFunctionDoubleToLongFunctionIntToDoubleFunctionIntToLongFunctionLongToDoubleFunctionLongToIntFunction
    • 二元:BiFunction

      • 原始类型

        • 作为返回值:ToDoubleBiFunctionToIntBiFunctionToLongBiFunction
  • Supplier类

    • 一元:Supplier

      • 原始类型

        • 作为返回值:BooleanSupplierDoubleSupplierIntSupplierLongSupplier
  • Consumer类

    • 一元:Consumer

      • 原始类型

        • 作为参数:DoubleConsumerIntConsumerLongConsumer
    • 二元:BiConsumer

      • 原始类型

        • 作为参数:ObjDoubleConsumerObjIntConsumerObjLongConsumer
  • Predicate类

    • 一元:Predicate

      • 原始类型

        • 作为参数:DoublePredicateIntPredicateLongPredicate
    • 二元:BiPredicate
  • Operator类

    • 一元:UnaryOperator

      • 原始类型

        • 同时作为参数和返回值:DoubleUnaryOperatorIntUnaryOperatorLongUnaryOperator
    • 二元:BinaryOperator

      • 原始类型

        • 同时作为参数和返回值:DoubleBinaryOperatorIntBinaryOperatorLongBinaryOperator

“额~彬哥,你这么把它们都列出来,搞得我更晕啦!”
别担心,对于它们每一类、每一个接口到底是什么?怎么用?咱们后面的章节都会一一具体说明。这个分类,一来,可以帮助大家先有个大体上的认识;二来,也可以作为后面章节的一个大纲,同时方便大家搞忘的时候来查阅。


RobynLiu
118 声望3 粉丝

百年后,工程、技术早已古老,但我们的技艺扔将受到尊重。