Lambda&Java多核编程-5-函数式接口与function包
从前面的总结中我们知道Lambda的使用场景是实现一个函数式接口,那么本篇就将阐述一下何为函数式接口以及Java的function包中提供的几种函数原型。
函数式接口
早期也叫作SAM(Single Abstract Interface),从全称能够看出是一种只定义了单个抽象方法的接口。
在这里,由于需要引入新的概念,故先来学习何为默认方法,再顺便提一下接口中的静态方法。
默认方法(Virtual Extension Methods)
也称为虚拟扩展方法、防护方法,由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中默认方法与静态方法不会干扰到目标方法的调用。
你也许注意到了接口声明上面的注解@FunctionalInterface,这是一种Java8中的注解,官方给出的解释:
An informative annotation type used to indicate that an interface type declaration is intended to be a functional interface as defined by the Java Language Specification.
也就是说这种注解主要用于传达一个信息,指明这个接口类型符合Java语言规范中对函数式接口的定义。
@FunctionalInterface用在IDE中能使我们更易判断自己定义的接口是否符合SAM规范,能否用于Lambda,因为如果不符合规范,IDE将直接给出错误提示。
正是函数式接口的这种规范性才能够使编译器正确推断出Lambda中到底调用了什么接口中的什么方法(即实现了哪个函数式接口),这个过程叫做目标类型,当然这种推断比我们现在想象的更为复杂,在以后的总结中我们将会深入研究这种机制。
在书写Lambda时,最基本的一点是保证参数类型、参数个数以及返回类型三者与接口中的目标方法的特征相符,这个特征叫做SAM的函数类型。
对于本例,方法既不接收参数,也无返回值,所以只需要简单地书写成:
new Koo().useTheInterface(() -> System.out.println("Yeah, defined in lambda!"));
就可以给予目标方法新的行为。
补充下useTheInterface()的定义:
private void useTheInterface(ExInterface exInterface) {
exInterface.targetMethod();
}
function包中的几种函数原型
在讲函数组合行为的那篇文章中(请见Lambda&Java多核编程-2-并行与组合行为),我们为了将提取比较键的行为变成一个可传递的参数,使用了一种函数原型:
Function<Contact, Character> 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>BooleanSupplierDoubleToLongFunctionIntBinaryOperatorObjDoubleConsumer<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!
Lambda&Java多核编程-5-函数式接口与function包的更多相关文章
- Lambda&Java多核编程-6-方法与构造器引用
在Lambda&Java多核编程-2-并行与组合行为一文中,我们对Stream<Contact>里的每一位联系人调用call()方法,并根据能否打通的返回结果过滤掉已经失效的项. ...
- Lambda&Java多核编程-7-类型检查
本篇主要介绍Lambda的类型检查机制以及周边的一些知识. 类型检查 在前面的实践中,我们发现表达式的类型能够被上下文所推断.即使同一个表达式,在不同的语境下也能够被推断成不同类型. 这几天在码一个安 ...
- 初探Lambda表达式/Java多核编程【0】从外部迭代到内部迭代
开篇 放假前从学校图书馆中借来一本书,Oracle官方的<精通Lambda表达式:Java多核编程>. 假期已过大半才想起来还没翻上几页,在此先推荐给大家. 此书内容及其简洁干练,如果你对 ...
- 初探Lambda表达式/Java多核编程【3】Lambda语法与作用域
接上一篇:初探Lambda表达式/Java多核编程[2]并行与组合行为 本节是第二章开篇,前一章已经浅显地将所有新概念点到,书中剩下的部分将对这些概念做一个基础知识的补充与深入探讨实践. 本章将介绍L ...
- 初探Lambda表达式/Java多核编程【1】从集合到流
从集合到流 接上一小节初探Lambda表达式/Java多核编程[0]从外部迭代到内部迭代,本小节将着手使用"流"这一概念进行"迭代"操作. 首先何为" ...
- 初探Lambda表达式/Java多核编程【2】并行与组合行为
今天又翻了一下书的目录,第一章在这之后就结束了.也就是说,这本书所涉及到的新的知识已经全部点到了. 书的其余部分就是对这几个概念做一些基础知识的补充以及更深层次的实践. 最后两个小节的内容较少,所以合 ...
- 初探Lambda表达式/Java多核编程【4】Lambda变量捕获
这周开学,上了两天感觉课好多,学校现在还停水,宿舍网络也还没通,简直爆炸,感觉能静下心看书的时间越来越少了...寒假还有些看过书之后的存货,现在写一点发出来.加上假期两个月左右都过去了书才看了1/7都 ...
- Lambda01 编程范式、lambda表达式与匿名内部类、函数式接口、lambda表达式的写法
1 编程范式 主要的编程范式有三种:命令式编程,声明式编程和函数式编程. 1.1 命令式编程 关注计算机执行的步骤,就是告诉计算机先做什么后做什么 1.2 声明式编程 表达程序的执行逻辑,就是告诉计算 ...
- Java的lamda表达式/函数式接口/流式计算
在我们看他人code的时候经常会看到,可能会经常看到lambda表达式,函数式接口,以及流式计算.在刚接触这些新功能时,也觉得真的有必要吗?但是现在写多了,发现这个功能确实能简化代码结构,提升编码效率 ...
随机推荐
- C++第四天学习
回顾: 1.初始化表 2.this指针 3.拷贝构造 Test(const Test& rt) { //1.分配新空间 //2.给新空间赋值 } 4.static成员 类::函数(): 类型 ...
- java中的静态代理和动态代理,入门整理
静态代理和动态代理主要解决的问题是:在直接访问对象时带来的问题,其目的就是为其他对象提供一个代理以控制对某个对象的访问.代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后 ...
- 【蒙地卡罗法求PI】
/* 蒙地卡罗法求PI 说明 蒙地卡罗为摩洛哥王国之首都,该国位于法国与义大利国境,以赌博闻名.蒙地卡罗的基本原理为以乱数配合面积公式来进行解题,这种以机 率来解题的方式带有赌博的意味,虽然在精确度上 ...
- 最快让你上手ReactiveCocoa之基础篇(简称RAC)
前言 很多blog都说ReactiveCocoa好用,然后各种秀自己如何灵活运用ReactiveCocoa,但是感觉真正缺少的是一篇如何学习ReactiveCocoa的文章,小编看了很多篇都没看出怎么 ...
- 小学生之Hibernate插入数据修改数据使用数据库默认值的实现
最近在写一个案例,定时任务对数据库进行更新操作,废话不多说,上代码: @Component("taskJob") public class TaskJob extends Hibe ...
- 源码(04) -- java.util.List<E>
java.util.List<E> 源码分析(JDK1.7) --------------------------------------------------------------- ...
- Swift 2.2 多态和强制转换
写在前面: 写点东西,就是想告诉自己,有时间其实你也在前进着,快慢不说,至少没停下吧!该有的都会有的.不瞎BB了,说主题,3.0 的多态和继承. 总觉得继承好像也没什么太多的可说的了,在项目中用到的还 ...
- Swift 2.0 单例的用法
单例我们项目中是很常用的,今天刚学了在swift中怎么写单例和调用单例.下面我们简单的介绍一下.我们先看看Swift单例的写法: import UIKit class Shareinstance: N ...
- 腾讯X5内核使用 Android WebView 的一些小问题
大家好,我是博客小白,第一篇文章,文笔不好,务喷,希望能给各位提供点帮助 公司做个商城,然后我就简单的做个启动引导页,然后用个原生WebView套一下,加个加载动画,解决下第三方登录支付的返回问题,这 ...
- MongoDB学习总结(六) —— 数据库备份和恢复
我们都知道数据库数据经常备份是多么的重要,MongoDB作为一个数据库系统,自然提供了完善,丰富而且好用的备份与恢复机制. 以下介绍三种数据库备份和恢复的方式 > 数据目录直接拷贝 数据库目录直 ...