lamda表达式与函数式接口
认识函数式接口
概述
在编程语言中,一等公民可以作为函数参数,可以作为函数返回值,也可以赋值给变量。
比如八大基本类型、自定义实体类等等,以前这些类型都是对客观世界实体的一个数据抽象,而在java7之后,函数也晋升为一等公民。它是对客观实体动作的一个行为抽象。
从我们对接口的定义上来说,我们实现了【用不同的数据,做一样的的事情】到【用不同的数据,做不同的事情】
的转变。
函数式接口只负责定义方法的参数个数、类型以及返回类型,实例化阶段相当于确定方法体。也就是说:当函数式接口被实例化之后,他就跟一个普通方法一样。所有东西都确定了。然后当实例调用了函数式接口里面那个唯一抽象方法之后,就相当于执行了一次该方法
函数式接口定义
何为函数式接口?
一个接口类型的类,有且只有一个抽象方法。
默认方法(default修饰)有实现,所以不是抽象的,隐藏不计入接口的抽象方法个数。
如果一个接口声明了一个抽象方法重写了java.lang的一个公共方法。它也不计入接口的抽象方法计数,因为接口的任何实现都有来自java.lang.Object或其他地方的实现。
@FunctionalInterface // 起标识和校验的作用
public interface MyFunctionInterface {
void run(); // 唯一的抽象方法
default String hello() { // 默认方法,不计入
return "hello!";
}
@Override
String toString(); // 重写了java.lang的一个公共方法,不计入
}
函数式接口的实例化方式
- lambda表达式
- 方法引用
- 构造方法引用
常见的函数式接口
无参有返回值类型Supplier
适用于没有入参,有返回值的行为抽象
/**
* 数据提供者。没有要求每次调用提供者时返回一个新的或不同的结果。
*/
@FunctionalInterface
public interface Supplier<T> {
T get();
}
有参无返回值类型Consumer
适用于接受单个输入参数但不返回结果的行为抽象
/**
* 数据消费者。表示接受单个输入参数但不返回结果的操作。
*/
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
/**
* Consumer对象调用,执行Consumer本身的行为,再执行after本身的行为,也就是一次执行两个 * 行为
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
二元消费者BiConsumer
适用于需要两个参数,但没有返回结果的行为抽象
/**
* 表示接受两个输入参数但不返回结果的操作。这就是Consumer的二元专业化。
*/
@FunctionalInterface
public interface BiConsumer<T, U> {
void accept(T t, U u);
/**
* 类似Consumer的andThen()
*/
default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after) {
Objects.requireNonNull(after);
return (l, r) -> {
accept(l, r);
after.accept(l, r);
};
}
}
有参有返回值类型Function
适用于有一个入参,并有返回值的行为抽象
/**
* 表示接受一个实参并产生结果的函数。
*/
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
/**
* 入参为一个Function类型函数式接口,接收参数,先执行before行为,将返回值作为入参,执行 * 本身的行为,然后将返回值返回
*/
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
/**
* 入参也是一个Function类型函数式接口,但与compose相反,接收参数,先本身行为,将返回值作 * 为after行为的入参,执行参数,然后将返回值返回
*
*/
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;
}
}
BiFunction
适用于需要两个参数,并有返回值的行为抽象
/**
* 表示接受两个参数并产生结果的函数。这就是Function的二元特殊化。
*/
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
/**
* 接收两个参数,执行本身行为,产生的返回值作为Function类型的after的入参,执行after的行 * 为将after的返回值返回
*/
default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t, U u) -> after.apply(apply(t, u));
}
}
UnaryOperator
Function特化,返回值类型与参数类型相同
@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {
static <T> UnaryOperator<T> identity() {
return t -> t;
}
}
判断类型Predicate
适用于对判定行为的抽象
/**
*
*/
@FunctionalInterface
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);
}
/**
* 返回一个谓词,该谓词根据对象测试两个参数是否相等。=(对象,对象)。 参数: targetRef——与 * 之比较是否相等的对象引用,它可能为空 类型参数: -谓词实参的类型 返回: 根据对象测试两个 * 参数是否相等的谓词。=(对象,对象)
*/
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
其他jdk提供FunctionInterface基本都是这四个类型的特化
应用场景
有入参有返回值
- 根据id查询name工具类
日常开发的配置表都会有配置的名称,有时候会对一批数据的id查询name返回给前端展示,此时可能会很多重复的id,为提高性能,我们通常的做法就是把id<->name关系放map缓存起来,不同表的id要到对应的表去查,根据【用不同的数据,做不同的事情】,我们可以把查询name的动作给抽象出来,在调用的时候实例化皆可。一下是工具类实现
public final class IdNameHelper {
private IdNameHelper() {
}
public static String getName(String id, Map<String, String> idNameMap, UnaryOperator<String> qryNameMethod) {
Objects.requireNonNull(qryNameMethod);
String name = idNameMap.get(id);
if (name == null) {
name = qryNameMethod.apply(id);
idNameMap.put(id, name);
}
return name;
}
}
- 不同的业务,定义不同的操作行为
Map<String, BiFunction<List<ParameterSpec>, List<Map<String, Object>>, Object>>
比如之前一个数据转换器,定义一个map,key为业务类型, value为一个行为,将各个场景的处理方法注册起来,然后根据业务类型执行不同的转逻辑。
- 发布 - 订阅
其实本质就是数据提供和数据消费,对应的Supplier和Consumer。