本文博主纯手敲,转载请注明出处。
由于最近项目中都是基于JDK8或JDK9,因此熟悉新版本中的特性尤其重要,这里主要讲解Java8中java.util.function下的的函数式接口声明。
谈到函数式接口,那不得不提的必然是@FunctionalInterface注解啦,望文知义,这个注解就是用在接口上的,被称为“功能接口”,那么什么是所谓的“功能接口”呢?又有哪些特性呢?下面一一道来。
功能函数就是有@FunctionInterface注解的接口,特性包括:
1、 接口中只能有一个抽象方法,但是可以有默认方法(被default修饰的方法),默认方法不是抽象方法,包含具体的方法体。这种设计主要是因为,对于功能接口来讲,一个接口只会提供一个逻辑上的方法,通过这个逻辑上的方法表达一个单一函数,单是指多个方法的其他方法需要是继承自Object的public方法,或者你要想绕过,就自己实现default。函数式接口自己本身一定是只有一个抽象方法。同时,如果是Object类的public方法,也是不允许的。官方的说明翻译如下: 如果一个接口I,I有一组抽象方法集合M,且这些方法都不是Object类的public签名方法,那么如果存在一个M中的方法m,满足:
m的签名是所有M中方法签名的子签名。
m对于M中的每个方法都是返回类型可替换的。 我的理解是说一个功能函数只能有一个实际方法的“映射”,下面举个例子说明:
首先,在一个功能接口中有且仅有一个非Object中的抽象方法
/**
* 有且仅有一个,而且是非Object里的方法
*/
@FunctionalInterface
public interface InterfaceTest {
boolean equals(Object obj);//会报错,因为equals是Object的方法
}
//下面这个就没问题了,因为有一个抽象方法,另外一个是Object的,可以共存
@FunctionalInterface
public interface InterfaceTest {
boolean equals(Object obj);
void foo();
}
//下面这个就又不行了,因为必须是Object的public方法才可以,clone是protected的
@FunctionInterface
public interface InterfaceTest(){
Object clone();
void foo();
}
复制代码
以上总结为一句话就是:函数式接口,有且仅有一个抽象方法,Object的public方法除外。
下面来看一下Function接口中的方法: Function接口只是提供了一个apply方法,传入E返回R,源码如下:
@FunctionalInterface
public interface Function<T, R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
}
复制代码
那么提供一个这种抽象方法有什么意义呢?我的理解是可以把它看做我们实际方法的一种“映射”,这又是什么意思呢?看例子:
public class UserDao(){
public User getUser(Integer userId){
User user = getUSer(userId);//getUser的方法实现不用考虑,这里只是为了说明问题
return user;
}
}
复制代码
我们看看这个例子中的getUser
方法是不是可以看做是Function中的applay方法的一个实现?传入的一种类型的参数,返回不同类型的结果。其实参数的类型和返回值类型也可以是相同的,即T可以与R相同。这里不说它是实现,我更喜欢称作它为“映射”。
那么有了这种映射,我们在调用这个getUser方法的时候就很方便了,可以使用lambda表达式,也可以使用方法引用了,因为lambda表达式和方法引用使用的前提就是必须有该方法对应的功能函数方法映射,哈哈,还是觉得映射比较合适。看下面例子:
import java.util.function.Function;
public class UserService {
public static void main(String[] args) {
UserService service = new UserService();
//lambda表达式写法
User lamUser = service.getUser(id->new User(id,"name"+id,"123"),1);
System.out.println(lamUser);
//函数引用写法
User user = service.getUser(UserDao::getUser, 1);
System.out.println(user);
}
public User getUser(Function<Integer, ? extends User> func,Integer id){
User user = func.apply(id);
return user;
}
}
复制代码
怎么样?到了这里是不是有点感觉了?嘿嘿,接着向下看,这才刚入门。。。
下面我们将看看Java8里java.util.function
包下的函数式接口。其中包括Function
、Predicate
、Supplier
、Consumer
、Operator
(没有Operator接口,只有类似BinaryOperator这样的接口)。
Function
Function的apply
方法前边我们已经看到过了,这里再贴一遍:
@FunctionalInterface
public interface Function<T, R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
}
复制代码
函数意为将参数T传递给一个函数,返回R。即
其默认实现了3个default方法,分别是compose
、andThen
和identity
,对应的函数表达为:compose
对应,体现嵌套关系;
andThen
对应,转换了嵌套的顺序;还有
identity
对应了一个传递自身的函数调用对应。从这里看出来,
compose
和andThen
对于两个函数f和g来说,f.compose(g)
等价于g.andThen(f)
。如果这里的关系式看的有点晕,那就解释一下:
Compose
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
复制代码
就是该方法中需要传入一个Function作为参数,在调用compose
方法时,先调用一下传入的Function的apply
方法,然后再调用本函数内的apply
方法。
andThen
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
复制代码
相信这个不用说也明白了,就是先调用传入的Function的apply
,再调用本接口中的applay
方法。
identity
static <T> Function<T, T> identity() {
return t -> t;
}
复制代码
这个方法就是一个静态方法,返回一个执行了apply()方法之后只会返回输入参数的函数对象。
看个例子吧:
public static void main(String[] args) {
Function<Integer, Integer> name = e -> e * 2;
Function<Integer, Integer> square = e -> e * e;
int value = name.andThen(square).apply(3);
System.out.println("andThen value=" + value);
int value2 = name.compose(square).apply(3);
System.out.println("compose value2=" + value2);
Object identity = Function.identity().apply("huohuo");
System.out.println(identity);
}
复制代码
限于篇幅,本文先讲到这里,下一篇我们一起来看看高阶函数。