Java 如何支持函数式编程?
.max((o1,?o2)?->?o1-o2);?//?max 终止操作:返回 Optional<Integer>?
System.out.println(result.get());?//?输出 2?
}?
}?
Lambda 表达式
前面提到 Java 引入 Lambda 表达式的主要作用是简化代码编写。实际上,我们也可以不用 Lambda 表达式来书写例子中的代码。我们拿其中的 map 函数来举例说明。
下面三段代码,第一段代码展示了 map 函数的定义,实际上,map 函数接收的参数是一个 Function 接口,也就是函数接口。第二段代码展示了 map 函数的使用方式。第三段代码是针对第二段代码用 Lambda 表达式简化之后的写法。实际上,Lambda 表达式在 Java 中只是一个语法糖而已,底层是基于函数接口来实现的,也就是第二段代码展示的写法。
//?Stream 类中 map 函数的定义:?
public?interface?Stream<T>?extends?BaseStream<T,?Stream<T>>?{?
<R>?Stream<R>?map(Function<??super?T,???extends?R>?mapper);?
//...省略其他函数...?
}?
//?Stream 类中 map 的使用方法示例:?
Stream.of("fo",?"bar",?"hello").map(new?Function<String,?Integer>()?{?
@Override?
public?Integer?apply(String?s)?{?
return?s.length();?
}?
});?
//?用 Lambda 表达式简化后的写法:?
Stream.of("fo",?"bar",?"hello").map(s?->?s.length());?
Lambda 表达式包括三部分:输入、函数体、输出。表示出来的话就是下面这个样子:
(a,?b)?->?{?语句 1;语句 2;...;?return?输出;?}?//a,b 是输入参数?
实际上,Lambda 表达式的写法非常灵活。上面给出的是标准写法,还有很多简化写法。比如,如果输入参数只有一个,可以省略 (),直接写成 a->{…};如果没有入参,可以直接将输入和箭头都省略掉,只保留函数体;如果函数体只有一个语句,那可以将{}省略掉;如果函数没有返回值,return 语句就可以不用写了。
Optional<Integer>?result?=?Stream.of("f",?"ba",?"hello")?
.map(s?->?s.length())?
.filter(l?->?l?<=?3)?
.max((o1,?o2)?->?o1-o2);?
//?还原为函数接口的实现方式?
Optional<Integer>?result2?=?Stream.of("fo",?"bar",?"hello")?
.map(new?Function<String,?Integer>()?{?
@Override?
public?Integer?apply(String?s)?{?
return?s.length();?
}?
})?
.filter(new?Predicate<Integer>()?{?
@Override?
public?boolean?test(Integer?l)?{?
return?l?<=?3;?
}?
})?
.max(new?Comparator<Integer>()?{?
@Override?
public?int?compare(Integer?o1,?Integer?o2)?{?
return?o1?-?o2;?
}?
});?
Lambda 表达式与匿名类的异同集中体现在以下三点上:
Lambda 就是为了优化匿名内部类而生,Lambda 要比匿名类简洁的多得多。
Lambda 仅适用于函数式接口,匿名类不受限。
即匿名类中的 this 是“匿名类对象”本身;Lambda 表达式中的 this 是指“调用 Lambda 表达式的对象”。
函数接口
实际上,上面一段代码中的 Function、Predicate、Comparator 都是函数接口。我们知道,C 语言支持函数指针,它可以把函数直接当变量来使用。
但是,Java 没有函数指针这样的语法。所以它通过函数接口,将函数包裹在接口中,当作变量来使用。实际上,函数接口就是接口。不过,它也有自己特别的地方,那就是要求只包含一个未实现的方法。因为只有这样,Lambda 表达式才能明确知道匹配的是哪个方法。如果有两个未实现的方法,并且接口入参、返回值都一样,那 Java 在翻译 Lambda 表达式的时候,就不知道表达式对应哪个方法了。
函数式接口也是 Java interface 的一种,但还需要满足:
一个函数式接口只有一个抽象方法(single abstract method);
Object 类中的 public abstract method 不会被视为单一的抽象方法;
函数式接口可以有默认方法和静态方法;
函数式接口可以用 @FunctionalInterface 注解进行修饰。
满足这些条件的 interface,就可以被视为函数式接口。例如 Java 8 中的 Comparator 接口:
@FunctionalInterface?
public?interface?Comparator<T>?{?
/**?
*?single?abstract?method?
*?@since?1.8?
*/?
int?compare(T?o1,?T?o2);?
/**?
*?Object 类中的 public?abstract?method??
*?@since?1.8?
*/?
boolean?equals(Object?obj);?
/**?
*?默认方法?
*?@since?1.8?
*/?
default?Comparator<T>?reversed()?{?
return?Collections.reverseOrder(this);?
}?
/**?
*?静态方法?
*?@since?1.8?
*/?
public?static?<T?extends?Comparable<??super?T>>?Comparator<T>?reverseOrder()?{?
return?Collections.reverseOrder();?
}?
//省略...?
}?
函数式接口有什么用呢?一句话,函数式接口带给我们最大的好处就是:可以使用极简的 lambda 表达式实例化接口。为什么这么说呢?我们或多或少使用过一些只有一个抽象方法的接口,比如 Runnable、ActionListener、Comparator 等等,比如我们要用 Comparator 实现排序算法,我们的处理方式通常无外乎两种:
规规矩矩的写一个实现了 Comparator 接口的 Java 类去封装排序逻辑。若业务需要多种排序方式,那就得写多个类提供多种实现,而这些实现往往只需使用一次。
另外一种聪明一些的做法无外乎就是在需要的地方搞个匿名内部类,比如:
public?class?Test?{??
public?static?void?main(String?args[])?{??
List<Person>?persons?=?new?ArrayList<Person>();?
Collections.sort(persons,?new?Comparator<Person>(){?
@Override?
public?int?compare(Person?o1,?Person?o2)?{?
return?Integer.compareTo(o1.getAge(),?o2.getAge());?
}?
});?
}??
}?
匿名内部类实现的代码量没有多到哪里去,结构也还算清晰。Comparator 接口在 Jdk 1.8 的实现增加了 FunctionalInterface 注解,代表 Comparator 是一个函数式接口,使用者可放心的通过 lambda 表达式来实例化。那我们来看看使用 lambda 表达式来快速 new 一个自定义比较器所需要编写的代码:
Comparator<Person>?comparator?=?(p1,?p2)?->?Integer.compareTo(p1.getAge(),?p2.getAge());?
-> 前面的 () 是 Comparator 接口中 compare 方法的参数列表,-> 后面则是 compare 方法的方法体。
下面将 Java 提供的 Function、Predicate 这两个函数接口的源码,摘抄如下:
@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));?
}?
def 《一线大厂 Java 面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》无偿开源 威信搜索公众号【编程进阶路】 ault?<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;?
}?
}?
@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)?{?
评论