java8 新特性推出的 Lambda 表达式,即函数式编程,相信很多开发胸弟都会使用了,但是什么是函数式编程呢?别问我,我也不知道标准的定义。其核心思想是:使用不可变值和函数,函数对一个值进行处理,映射成另一个值。
函数接口
java8之前接口类只有方法的定义,没有实现的,Java8对接口提供默认方法的新特性。一个接口类可以定义n个抽象方法,但如果有 @FunctionalInterface 注解修饰就不一样了,该注释会强制编译检查一个接口是否符合函数接口的标准。如果该注释添加给一个枚举类型、类或另一个注释,或者接口包含不止一个抽象方法,编译就会报错。@FunctionalInterface 注解修饰的接口就是被定义成函数接口。
常用的函数接口
平时开发中常用的函数接口有无返回值的Consumer,返回值为Boolean的Predicate,把入参T映射成R返回值的Function 和返回实例对象的Supplier。接下来我们一起分析这四个函数接口类的源码以及简单的使用,先创建一个后面需要用到的实体类 Griez。
public class Griez implements Serializable {
private int age;
private String name;
public Griez(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public Griez setAge(int age) {
this.age = age;
return this;
}
public String getName() {
return name;
}
public Griez setName(String name) {
this.name = name;
return this;
}
}
定义一个法国球星格子(我最喜欢的球员)类,为了简单这里只有名字和年龄,他的能力值和薪资就不展示出来了,怕吓到大家。
Consumer
@FunctionalInterface
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); };
}
}
Consumer函数接口有两个方法,抽象方法accept需要调用者自己实现;andThen默认方法封装了两个Consumer执行顺序的Consumer实例,先执行本实例的accept,再执行参数after的accept方法。
public static void main(String[] args) {
Consumer<Griez> consumer = griez -> System.out.println(griez.getName() + griez.age);
Consumer<Griez> afterConsumer = griez -> System.out.println(griez.getAge());
Consumer<Griez> then = consumer.andThen(afterConsumer);
then.accept(new Griez(29, "wolf"));
}
第1,2行创建Consumer实例,并实现了accept方法。记得刚毕业出来面试有个面试官问我接口能不能创建实例,我给他的回答是:“你说呢?”。
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);
}
// 对象判断?
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
test 方法需要自定义逻辑,返回一个boolean值,该函数接口是做判断用途的。and 方法是两个Predicate 逻辑与判断;negate 方法是逻辑非判断;or 方法是两个 Predicate 逻辑或判断;isEqual 方法是判断targetRef是否与本实例相同。
public static void main(String[] args) {
Predicate<Griez> predicate= griez -> griez.getAge() > 18;
System.out.println("Griez已经成年了 : " + predicate.test(new Griez(29, "griez")));
}
test返回Boolean值。
Function
@FunctionalInterface
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;
}
}
apply 方法接收一个T参数,然后一个R,该方法是类型映射作用。compose 方法是组装了两个Function执行顺序行为Function对象,先执行参数的Function在执行本实例Function。andThen 跟 compose执行顺序相反。identity 方法是返回参数本身。
public static void main(String[] args) {
Function function = o -> new Griez(20, "Female");
System.out.println(function.apply(new Griez(29, "griez").getName()));
}
一个大老爷进去变成一个年轻小姐姐出来,变态的程序员。
Supplier
@FunctionalInterface
public interface Supplier<T> {
T get();
}
该函数接口看上去就比较寒颤了,只有一个方法。get 方法作用如名字一个,伸手党来的,获取一个实例。
public static void main(String[] args) {
Supplier<Griez> supplier = () -> new Griez(29, "griez");
System.out.println(supplier.get());
}
获取一个Griez对象。