从前面的总结中我们知道 Lambda 的使用场景是实现一个函数式接口,那么本篇就将阐述一下何为函数式接口以及 Java 的
包中提供的几种函数原型。
- function
早期也叫作 SAM(Single Abstract Interface),从全称能够看出是一种只定义了单个抽象方法的接口。
在这里,由于需要引入新的概念,故先来学习何为默认方法,再顺便提一下接口中的静态方法。
也称为虚拟扩展方法、防护方法,由 Java8 引入,意味着现在接口能够实现自身所声明的方法。
看一个示例:
- public interface ExInterface {
- default void doSomething() {
- System.out.println("I did something :)");
- }
- }
如此我们就成功地给
方法赋予了默认操作,注意方法声明前的
- doSomething()
关键字,表明这个方法拥有默认操作,如果不添加
- default
将导致的错误。
- default
对应地我们给出一个实现了这个接口的类:
- public class Koo implements ExInterface {
- public static void main(String[] args) {
- new Koo().doSomething();
- }
- }
可以看出我们并没有像以往那样必须重写接口中的抽象方法,而是直接调用了
,此时代码将会执行定义在接口中的默认方法体:
- doSomething()
- I did something :)
根据常识,只要我们在该类中重定义这个接口中的方法:
- @Override
- public void doSomething() {
- System.out.println("I did something different :)");
- }
调用时就会执行重写后的方法体:
- I did something different :)
此时出现了一个问题,如果类实现了两个接口,但是两个接口中都有相同名称的防护方法,在进行默认调用时就会出现冲突,这种情况下我们只好先在类内覆盖掉两接口中同名的防护方法,再手动指定调用哪一个:
- @Override
- public void doSomething() {
- ExInterface.super.doSomething();
- }
跟一些类的静态方法应该没什么不同,在接口中定义静态方法时必须同时给出这个方法的实现,换句话说就是必须给出方法体:
- static void doStaticThings() {
- System.out.println("I did some static things :)");
- }
值得注意的是,即使静态方法被定义在接口中,在其他类里也不能重写这个方法,因为它在实现接口时被隐藏了,这一点跟子类无法重写父类中的静态方法是相似的。
说了这两个额外的概念,现在回到正题。
根据函数式接口的定义,以上我们提到的两种接口方法均不能作为 SAM 的目标方法,也就是说,我们必须再在接口中定义一个纯纯的虚方法才能使之成为真正的 SAM:
- @FunctionalInterface
- public interface ExInterface {
- default void doSomething() {
- System.out.println("I did something :)");
- }
- static void doStaticThings() {
- System.out.println("I did some static things :)");
- }
- void targetMethod();
- }
从此也可以得出结论,SAM 中默认方法与静态方法不会干扰到目标方法的调用。
你也许注意到了接口声明上面的注解
,这是一种 Java8 中的注解,官方给出的解释:
- @FunctionalInterface
也就是说这种注解主要用于传达一个信息,指明这个接口类型符合 Java 语言规范中对函数式接口的定义。
用在 IDE 中能使我们更易判断自己定义的接口是否符合 SAM 规范,能否用于 Lambda,因为如果不符合规范,IDE 将直接给出错误提示。
- @FunctionalInterface
正是函数式接口的这种规范性才能够使编译器正确推断出 Lambda 中到底调用了什么接口中的什么方法(即实现了哪个函数式接口),这个过程叫做目标类型,当然这种推断比我们现在想象的更为复杂,在以后的总结中我们将会深入研究这种机制。
在书写 Lambda 时,最基本的一点是保证参数类型、参数个数以及返回类型三者与接口中的目标方法的特征相符,这个特征叫做 SAM 的函数类型。
对于本例,方法既不接收参数,也无返回值,所以只需要简单地书写成:
- new Koo().useTheInterface(() -> System.out.println("Yeah, defined in lambda!"));
就可以给予目标方法新的行为。
补充下
的定义:
- useTheInterface()
- private void useTheInterface(ExInterface exInterface) {
- exInterface.targetMethod();
- }
包中的几种函数原型
- function
在讲函数组合行为的那篇文章中(请见),我们为了将提取比较键的行为变成一个可传递的参数,使用了一种函数原型:
- Function keyExtractor = o -> o.getFirstName().charAt(0);
这个接口就是来自于此包,当然前几篇文章中用到的很多原型都是如此。
现在我们把包中最基本的四种原型列出来:
- Consumer<T>
- Predicate<T>
- Supplier<T>
- Function<T, U>
第一种,Consumer,字面意思就是消费者,把你提供的参数吃掉了,什么也没留,因此返回类型为
,但是你需要提供一个类型为
- void
的参数,如:
- T
- s -> {
- new Me.eat(s);
- System.out.println("Nothing left :P");
- }
第二种,Predicate,字面意为谓词(哈哈刚学过离散),你得提供一个类型为
的参数,由谓词来判断,然后返回一个
- T
类型的结果, 如:
- boolean
- indianMiFans -> new LeiJun.areYouOK(indianMiFans);
第三种,Supplier,即提供者 / 供应商,凭空给你个好东西,也就是说你不需要提供任何参数,但它能够返回一个类型为
的返回值,如:
- T
- () -> Hub.getCargo();
最后一种,Function,顾名思义——函数,提供
类型的参数,返回
- T
类型的返回值,其本身就相当于一个算子,如:
- U
- param -> MyMath.doCalculation(param);
当然在前面的文章中我们同样遇到过
之类的接口,前者代表一元操作符。
- UnaryOperator
很明显,只提供这几种接口是不能满足我们的需要的,所以经过原生特化、变化参数数量与两者相混合这三种方式,能够得到更多的函数式接口:
- BiConsumer<T,U>
- BiFunction<T,U,R>
- BooleanSupplier
- DoubleToLongFunction
- IntBinaryOperator
- ObjDoubleConsumer<T>
...
- IntToDoubleFunction
这些接口就不做具体介绍了,感兴趣可以直接查阅 Java API 文档。
这个库相当于 Java8 的,你能够用库中的东西轻松实现自己的 SAM,当然在改进程序细粒度时你也需要使用它们,就像在前几篇文章中那样,分拆并组合一些函数行为。
不说了,饭还没吃,晚上选修怕迟到:)
没有总结,提供下本篇的代码。
ExInterface.java
- @FunctionalInterface
- public interface ExInterface {
- default void doSomething() {
- System.out.println("I did something :)");
- }
- static void doStaticThings() {
- System.out.println("I did some static things :)");
- }
- void targetMethod();
- }
Koo.java
- public class Koo implements ExInterface {
- @Override
- public void doSomething() {
- ExInterface.super.doSomething();
- }
- @Override
- public void targetMethod() {
- }
- public static void main(String[] args) {
- new Koo().doSomething();
- ExInterface.doStaticThings();
- new Koo().useTheInterface(() -> System.out.println("Yeah, defined in lambda!"));
- }
- private void useTheInterface(ExInterface exInterface) {
- exInterface.targetMethod();
- }
- }
以及它的运行结果:
- I did something :)
- I did some static things :)
- Yeah, defined in lambda!
来源: http://www.cnblogs.com/hwding/p/6531947.html