- 在上一节中,我们为了使用 Lambda 表达式不得不创建了各种函数描述符的函数式接口,其实 Java 8 已经给我们提供了一套能够描述常见函数描述符的函数式接口
- 比如
Predicate<T>
、Consumer<T>
、Function<T,R>
、Supplier<T>
等,这些函数式接口位于java.util.function
包- 这一节主要记录这些函数式接口的应用
Java8中的函数式接口
- 下面的表格列出了 Java8 中常见的函数式接口:
函数式接口 |
函数描述符 |
原始类型特化 |
Predicate |
T -> boolean |
IntPredicate, LongPredicate, DoublePredicate |
Consumer |
T -> void |
IntConsumer, LongConsumer, DoubleConsumer |
Function<T, R> |
T -> R |
IntFunction, IntToDoubleFunction, IntToLongFunction, LongFunction, LongToDoubleFunction, LongToIntFunction, DoubleFunction, ToIntFunction, ToDoubleFunction, ToLongFunction |
Supplier |
() -> T |
BooleanSupplier, IntSupplier, LongSupplier, DoubleSupplier |
UnaryOperator |
T -> T |
IntUnaryOperator, LongUnaryOperator, DoubleUnaryOperator |
BinaryOperator |
(T, T) -> T |
IntBinaryOperator, LongBinaryOperator, DoubleBinaryOperator |
BiPredicate<L, R> |
(L, R) -> boolean |
|
BiConsumer<T, U> |
(T, U) -> void |
ObjIntConsumer, ObjLongConsumer, ObjDoubleConsumer |
BiFunction<T, U, R> |
(T, U) -> R |
ToIntBiFunction<T, U>, ToLongBiFunction<T, U>, ToDoubleBiFunction<T, U> |
Predicate
- predicate: 英 [ˈpredɪkət] 美 [ˈpredɪkət] 断言,断定的意思
- 从接口的名称就可以推断出这个函数式接口的主要作用就是用于判断作用,Predicate 源码如下所示:
public interface Predicate<T> {
boolean test(T t);
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
default Predicate<T> negate() {
return (t) -> !test(t);
}
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
- 可以看到
java.util.function.Predicate<T>
接口定义了一个名叫test
的抽象方法,它接受一个泛型T
对象,并返回一个boolean
,函数描述符为(T) -> boolean
举几个例子- 偶数判断:
/**
* @author BNTang
**/
public class Demo {
public static void main(String[] args) {
Predicate<Integer> isEven = (in) -> in % 2 == 0;
System.out.println(isEven.test(17));
}
}
- 判断字符串的长度是否为0
/**
* @author BNTang
**/
public class Demo {
public static void main(String[] args) {
Predicate<String> isEmptyString = String::isEmpty;
System.out.println(isEmptyString.test(""));
}
}
- 除了抽象方法外,
java.util.function.Predicate<T>
接口还定义了三个默认方法:and
,negate
和or
,对应 “与”,“非” 和 “或” 操作,这样我们便可以复合 Lambda 表达式了,比如:
- 判断是偶数,并且大于30
/**
* @author BNTang
**/
public class Demo {
public static void main(String[] args) {
Predicate<Integer> isEven = (in) -> in % 2 == 0;
System.out.println(isEven.and((in) -> in > 30).test(40));
}
}
- 奇数判断
/**
* @author BNTang
**/
public class Demo {
public static void main(String[] args) {
Predicate<Integer> isEven = (in) -> in % 2 == 0;
Predicate<Integer> isOdd = isEven.negate();
System.out.println(isOdd.test(17));
}
}
Consumer
- 英 [kənˈsju:mə(r)] 美 [kənˈsu:mə(r)] n.消费者
- 该函数式接口用于消费一个对象,即接收一个对象,对其执行某些操作,然后没有返回值
- Consumer 源码如下所示:
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
- 可以看到
java.util.function.Consumer<T>
定义了一个名叫accept
的抽象方法,它接收一个泛型T
的对象,没有返回值(void
),函数描述符为(T) -> void
- 还提供了一个默认方法
andThen
- 举个例子:
/**
* @author BNTang
**/
public class Demo {
public static void main(String[] args) {
Consumer<Apple> printAppleColor = (a) -> System.out.println(a.getColor());
printAppleColor.accept(new Apple("red", 17.0));
printAppleColor.andThen((a) -> System.out.println(a.getWeight())).accept(new Apple("red", 17.0));
}
}
- 输出结果如下:
red
red
17.0
Supplier
- supplier 英 [səˈplaɪə(r)] 美 [səˈplaɪər] n.供应商;供应者;供给者
- 其源码如下:
public interface Supplier<T> {
T get();
}
- 可看到
java.util.function.Supplier<T>
定义了一个名叫get
的抽象方法,它不接收参数,返回一个泛型T
的对象,函数描述符为() -> T
- 举个例子,下面的代码中需要给
Apple
提供一个无参的构造函数:
/**
* @author BNTang
**/
public class Demo {
public static void main(String[] args) {
Supplier<Apple> personSupplier = Apple::new;
// new Person
personSupplier.get();
}
}
Function
- Function 源码如下:
public interface Function<T, R> {
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}
-
java.util.function.Function<T, R>
接口定义了一个叫作apply
的方法,它接收一个泛型T
的对象,并返回一个泛型R
的对象,函数描述符为(T) -> R
- 举个例子:
/**
* @author BNTang
**/
public class Demo {
public static void main(String[] args) {
Function<Apple, Double> getAppleWeight = (a) -> {
return a.getWeight();
};
System.out.println(getAppleWeight.apply(new Apple(17.0)));
}
}
- Function 接口还提供了两个抽象方法
compose
和andThen
,从源码中可以看出两者的根本区别- 举个
compose
例子:
/**
* @author BNTang
**/
public class Demo {
public static void main(String[] args) {
Function<Integer, Integer> f = (x) -> x + 1;
Function<Integer, Integer> g = (x) -> x * 2;
System.out.println(f.compose(g).apply(2));
}
}
- 过程为:
f(g(2))
,也就是1+(2*2)
- 举个
andThen
的例子:
/**
* @author BNTang
**/
public class Demo {
public static void main(String[] args) {
Function<Integer, Integer> f = (x) -> x + 1;
Function<Integer, Integer> g = (x) -> x * 2;
System.out.println(f.andThen(g).apply(2));
}
}
- 过程为:
g(f(2))
,也就是(2+1)*2
原始类型特化
- 在学习 Function 接口的时候,我们定义了
f
函数:
Function<Integer, Integer> f = (x) -> x + 1;
- x 的类型为 Integer 类型,1 为 int 类型,返回值为 Integer 类型,整个过程实际上为
Integer.valueOf(x.intValue() + 1)
- 虽然编译器可以自动帮我们完成拆装箱,但这会造成不必要的性能消耗
- 考虑到了这一点,Java8 为我们提供了 int 类型的 Function接口:
IntFunction
public interface IntFunction<R> {
R apply(int value);
}
- 所以
f
最好重构为如下的方式:
IntFunction<Integer> f = (x) -> x + 1;
- 剩余的原始类型特化函数式接口可参考上面的表格:
Java8中增强的Comparator
- 在 Java8 之前,Comparator 接口用于实现简单的比较排序算法
- 比如有如下List:
List<Double> list = new ArrayList<>();
list.add(12.3);
list.add(100.2);
list.add(3.14);
list.add(27.7);
list.add(-9.8);
- 使用 Comparator 接口对 List 从小到大排序:
/**
* @author BNTang
**/
public class Demo {
public static void main(String[] args) {
List<Double> list = new ArrayList<>();
list.add(12.3);
list.add(100.2);
list.add(3.14);
list.add(27.7);
list.add(-9.8);
Collections.sort(list, new Comparator<Double>() {
public int compare(Double o1, Double o2) {
return o1.compareTo(o2);
}
});
System.out.println(list);
}
}
- Comparator 接口也是一个函数式接口,函数描述符为
(T, T) -> int
,Java8 中可以使用 Lambda 改造上面的排序方法:
/**
* @author BNTang
**/
public class Demo {
public static void main(String[] args) {
...
Collections.sort(list, (o1, o2) -> o1.compareTo(o2));
...
}
}
- Java8 对 List 提供了
sort
方法,可以替代Collections.sort
,所以上面的代码还可以简化为:
/**
* @author BNTang
**/
public class Demo {
public static void main(String[] args) {
...
list.sort((o1, o2) -> o1.compareTo(o2));
...
}
}
- 使用
方法的引用
来进一步简化:
/**
* @author BNTang
**/
public class Demo {
public static void main(String[] args) {
...
list.sort(Double::compareTo);
...
}
}
- Java8 对 Comparator 进行了增强,加入了一些实用的默认方法,比如对排序结果反转:
/**
* @author BNTang
**/
public class Demo {
public static void main(String[] args) {
...
Comparator<Double> comparator = Double::compareTo;
list.sort(comparator.reversed());
...
}
}
- 更多方法可以参考 Comparator 接口的 JavaDoc
查看 Comparator 的时候发现其虽然是函数式接口,但是却包含了 compare
和 equals
这两个抽象方法,顿时有点懵逼,函数式接口不是只能有一个抽象方法么?查找资料后发现:函数式接口中可以额外定义多个抽象方法,但这些抽象方法签名必须和 Object 的 public 方法一样,接口最终有确定的类实现,而类的最终父类是 Object。因此函数式接口可以定义 Object 的 public 方法。