之前已经介绍过了Java8函数式变成及Lambda表达式,感兴趣可以看看,地址:Java8函数式编程(Lambda表达式)_琅琊之榜PJ的博客-CSDN博客
本文主要介绍一下常用的接口及用法,先来看一个表格:
本文主要选取几个常用的:Supplier
在此之前,再来了解一下@FunctionalInterface注解。
一种信息注释类型,用于指示接口类型声明是Java语言规范定义的函数接口。从概念上讲,函数接口只有一个抽象方法。由于默认方法有一个实现,所以它们不是抽象的。如果一个接口声明了一个覆盖java.lang.Object的一个公共方法的抽象方法,那么这也不计入该接口的抽象方法计数,因为该接口的任何实现都将具有来自java.lang.Oobject或其他地方的实现。
注意,函数接口的实例可以使用lambda表达式、方法引用或构造函数引用创建。
如果使用此注解类型对类进行注解,则编译器需要生成错误消息,除非类型是接口类型,而不是注解类型、枚举或类。
带注解的类型满足功能接口的要求。
但是,无论接口声明中是否存在FunctionalInterface注释,编译器都会将符合函数接口定义的任何接口视为函数接口。
源码如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}
需要注意的是:函数式接口只有一个抽象方法,接口声明覆盖 java.lang.Object的抽象方法,不会进行抽象方法计数。这对后面介绍几个常用的接口很有用!!
先看一下源码:
/**
* 表示结果的供应者。
* 没有要求每次调用供应者时都返回新的或不同的结果。
*/
@FunctionalInterface
public interface Supplier {
/**
* Gets a result.
*/
T get();
}
Supplier
示例:每次调用get()方法都返回一个整数:
Supplier supplier = new Supplier() {
@Override
public Integer get() {
return new Random().nextInt();
}
};
System.out.println(supplier.get());
结合Lambda表达式应用:
List integers = makeList(10, () -> (int) (Math.random() * 10));
integers.forEach(System.out::println);
private List makeList(int num, Supplier supplier){
List list = new ArrayList<>();
for (int i = 0; i < num; i++) {
list.add(supplier.get());
}
return list;
}
结合方法引用应用:
Supplier supplier1 = Math::random;
System.out.println(supplier1.get());
Java源码:
@FunctionalInterface
public interface Consumer {
void accept(T t);
default Consumer andThen(Consumer super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
Consumer
该类有两个方法
该方法对给定的参数执行此操作。
返回一个组合的Consumer,该Consumer依次执行此操作和after操作。如果执行任何一个操作都会引发异常,则会将异常中继到组合操作的调用方。如果执行此操作会引发异常,则不会执行after操作。
该方法消费一个指定泛型的数据:
import java.util.function.Consumer;
public class Demo01Consumer {
public static void main(String[] args) {
consumerString(s -> System.out.println(s));
}
private static void consumerString(Consumer function) {
function.accept("Hello");
}
}
运行程序,控制台输出:Hello
如果一个方法的参数和返回值全都是 Consumer 类型,那么就可以实现效果:消费数据的时候,首先做一个操作, 然后再做一个操作,实现组合。而这个方法就是 Consumer 接口中的default方法 andThen 。
default Consumer andThen(Consumer super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
提示:java.util.Objects 的 requireNonNull 静态方法将会在参数为null时主动抛出 NullPointerException 异常。这省去了重复编写if语句和抛出空指针异常的麻烦。
要想实现组合,需要两个或多个Lambda表达式即可,而 andThen 的语义正是“一步接一步”操作。例如两个步骤组合的情况:
import java.util.function.Consumer;
public class Demo02Consumer {
public static void main(String[] args) {
consumerString(
// toUpperCase()方法,将字符串转换为大写
s -> System.out.println(s.toUpperCase()),
// toLowerCase()方法,将字符串转换为小写
s -> System.out.println(s.toLowerCase())
);
}
private static void consumerString(Consumer one, Consumer two) {
one.andThen(two).accept("Hello");
}
}
运行结果将会首先打印完全大写的HELLO,然后打印完全小写的hello。当然,通过链式写法可以实现更多步骤的组合。
源码如下:
@FunctionalInterface
public interface Function {
R apply(T t);
default Function compose(Function super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default Function andThen(Function super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static Function identity() {
return t -> t;
}
}
Function
Function 接口是一个功能型接口,是一个转换数据的作用。接收一个T参数,返回一个R结果
Function 接口实现 apply 方法来做转换。
一个简单的示例:
Function function = p -> p.length() == 5;
Stream stream = stringList().stream().map(function);
默认方法1:
返回一个组合函数,该函数首先将before函数应用于其输入,然后将此函数应用于结果。如果对任意一个函数的求值引发异常,则会将异常中继到组合函数的调用方。
默认方法2:
返回一个组合函数,该函数首先将此函数应用于其输入,然后将after函数应用于结果。如果对任意一个函数的求值引发异常,则会将异常中继到组合函数的调用方。
静态方法:static
返回一个始终返回其输入参数的函数。
源码:
@FunctionalInterface
public interface Predicate {
boolean test(T t);
default Predicate and(Predicate super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
default Predicate negate() {
return (t) -> !test(t);
}
default Predicate or(Predicate super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static Predicate isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
Predicate
boolean test(T t):对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值
default Predicate
default Predicate
default Predicate
Predicate
最常见的应用是在集合Stream流过滤条件中使用:
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class LambdaStudy
{
public static void main(String[] args) {
//初始化list集合
List list = new ArrayList();
list.add("测试数据1");
list.add("测试数据2");
list.add("测试数据3");
list.add("测试数据12");
//使用λ表达式遍历集合
list.forEach(s -> System.out.println(s));
//结合Predicate使用和过滤条件筛选元素
Predicate contain1 = n -> n.contains("1");
Predicate contain2 = n -> n.contains("2");
//根据条件遍历集合
list.stream().filter(contain1).forEach(n -> System.out.println(n));
list.stream().filter(s -> contain1.test(s)).forEach(s -> System.out.println(s));
list.stream().filter(contain1.and(contain2)).forEach(n -> System.out.println(n));
list.stream().filter(contain1.or(contain2)).forEach(n -> System.out.println(n));
//将过滤后的元素重新放到一个集合中
List newList = list.stream().filter(contain1.and(contain2)).collect(Collectors.toList());
newList.forEach(s -> System.out.println(s));
}
}