前言
hello大家好,好久不见,前两天把手里的项目进度提了一下,今天有时间来继续复习JAVA基础了。今天的主角就是Function接口,老规矩先把源码贴出来。
1.源码
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Function<T, R> {
R apply(T var1);
default <V> Function<V, R> compose(Function<? super V, ? extends T> var1) {
Objects.requireNonNull(var1);
return (var2) -> {
return this.apply(var1.apply(var2));
};
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> var1) {
Objects.requireNonNull(var1);
return (var2) -> {
return var1.apply(this.apply(var2));
};
}
static <T> Function<T, T> identity() {
return (var0) -> {
return var0;
};
}
}
看过前两篇文章的同学们应该有印象,Consumer和Predicate接口都只有一个T作为一个输入泛型,而现在看起来Function接口是有一个T输入泛型和一个R输出泛型,关于泛型我稍微的再介绍一下。
(1)泛型类
泛型类使用<T>
来表示该类为泛型类,其内部成员变量和函数的返回值都可以为泛型<T>
,Function源码的标识为<T,R>
,也就是两个泛型参数,此处不再赘述,具体泛型类可以看网上的文章。
(2)泛型方法和通配符
在方法修饰符的后面加一个<T>
表明该方法为泛型方法,如Function 的源码里的compose方法的<V>
。通配符也很好理解,还是compose的例子,我们可以看到compose的参数为一个Function类型,其中Functin的参数指定了其第一个参数必须是V的父类,第二个参数必须继承T,也就是T的子类。
2. compose 和 andThen
其实对于Function接口来说,本身就是一个“处理工厂”的概念,它的意义在于我写了一个Function,意味着我准备把这个输入的“泛型”通过“某种处理”来输出其他“泛型”。这个接口是最能提现出JAVA8的函数式编程思想的,例如
y = f(x)
只不过在java中我先把函数写出来,然后再通过lambda表达式传入具体函数的实现方法。接下来我们对一个整数进行两个Function操作:
1. 将该int类型的数乘以10;
2. 将该int类型的数减去5;
package mytest;
import java.util.function.Function;
public class StringBuilderTest {
public static void main(String[] args) {
//将数乘10
Function<Integer, Integer> function1 = integer -> integer * 10;
//将数减5
Function<Integer, Integer> function2 = integer -> integer - 5;
System.out.println(function1.compose(function2).apply(10));
System.out.println(function1.andThen(function2).apply(10));
}
}
上面我们分别使用了compose和andThen方法,大家大可自己猜测一下两个结果是什么。
50
95
3. identity
这是一个静态方法,意味着可以使用 Function.identity() 这种方法调用。
static <T> Function<T, T> identity() {
return (var0) -> {
return var0;
};
}
这个方法返回的是 t -> t 的一个lambda表达式,有种“如蜜传如蜜”的感觉,哈哈哈哈。具体可以使用在哪儿呢?
下面的代码中,Task::getTitle
需要一个task并产生一个仅有一个标题的key。task -> task
是一个用来返回自己的lambda表达式,上例中返回一个task。
private static Map<String, Task> taskMap(List<Task> tasks) {
return tasks.stream().collect(toMap(Task::getTitle, task -> task));
}
可以使用Function接口中的默认方法identity来让上面的代码代码变得更简洁明了、传递开发者意图时更加直接,下面是采用identity函数的代码。
import static java.util.function.Function.identity;
private static Map<String, Task> taskMap(List<Task> tasks) {
return tasks.stream().collect(toMap(Task::getTitle, identity()));
}
作为函数式编程的第三个接口,特点就是“有两个入参,分别确定入参和出参的泛型,返回一个类”。
下一章我们继续学习Supplier接口