Java 8 函数式设计,更优雅的使用 Lambda
项目环境
- Java 8
- Spring 5.0+
- GitHub 地址:https://github.com/huajiexiewenfeng/java-base
- functional 模块
1.前言
本文是函数式接口的基础篇,学完本篇将有助于大家
- 看懂大神的代码
- 新增程序设计的新技能
- 更好的使用 Java 8 新特性,lambda 语法
2.什么是函数式接口 @FunctionalInterface
@FunctionalInterface
用于函数式接口类型声明的信息注解类型,这些接口的实例被 Lambda 表达式、方法引用或者构造器引用创建。函数式接口只能有一个抽象方法,并排除接口默认方法以及声明中覆盖 Object 的公开方法的统计。同时,@FunctionalInterface 不能标注在注解、类以及枚举上。如果违背以上规则,那么接口不能视为函数式接口,当标注 @FunctionalInterface 后,会引起编译错误。
不过,如果任意接口满足以上函数式接口的要求,无论接口是否声明中是否标注 @FunctionalInterface ,均能被编译器视作函数式接口。
示例
public class FunctionalInterfaceDemo {
public static void main(String[] args) {
Function1 function1 = ()->{
};
FunctionalInterfaceWithoutAnnotation function2 = ()->{
};
}
@FunctionalInterface
public interface Function1 {
void execute();
default String getDescription(){
return String.valueOf(this);
}
}
// @FunctionalInterface 并非必选
public interface FunctionalInterfaceWithoutAnnotation{
void execute();
}
}
- Function1 中可以看到,如果接口中有多个方法,保留一个接口抽象方法,其他方法必须添加 default 默认实现
- 如果接口只有一个方法,即使不写 @FunctionalInterface 也会被识别成函数式接口
- 符合以上条件的接口实例,可以使用 Lambda 表达式来创建
通过上面的例子我们似乎看到了一点点 Lambda 和 @FunctionalInterface 的联系,接下来我们将一步一步来了解 Java 8 中内置的几种函数式接口类型。
3.函数式接口类型
- 提供类型 - Supplier<T>
- 消费类型 - Consumer<T>
- 转换类型 - Function<T,R>
- 断定类型 - Predicate<T>
- 隐藏类型 - Action
4.举例说明
泛型类型参数命名约定
- E:表示集合元素(Element)
- V:表示数值(Value)
- K:表示键(Key)
- T:表示类型(Type)
- R:表示结果(Result)
- S:表示来源(Source)
4.1 Supplier
读音 /səˈplaɪər/
4.1.1 Supplier 接口定义
- 基本特点:只出不进
- 编程范式:作为方法/构造参数、方法返回值
- 使用场景:数据来源,代码替代接口
4.1.2 Supplier 接口源码
@FunctionalInterface
public interface Supplier<T> {
T get();
}
4.1.3 示例
public class SupplierDemo {
public static void main(String[] args) {
Supplier<Long> supplier = getLong();
System.out.println(supplier.get());
}
/**
* 第一种写法
* @return
*/
public static Supplier<Long> getLong(){
return ()->{
return System.currentTimeMillis();
};
}
/**
* 第二种写法
* @return
*/
public static Supplier<Long> getLongOther(){
return System::currentTimeMillis;
}
}
4.1.4 设计
普通版本
public class SupplierDesignDemo {
public static void main(String[] args) {
ehco("Hello,World");
}
public static void ehco(String message) {
System.out.println(message);
}
}
Supplier+Lambda 表达式
private static void level2() {
Supplier<String> supplyMessage = () -> {
return "lambda-Hello,World";
};
ehco(supplyMessage.get());
}
Supplier 作为 ehco 方法参数
private static void level3() {
ehco(SupplierDesignDemo::getMessage);
}
public static void ehco(Supplier<String> message) {
System.out.println(message.get());
}
public static String getMessage() {
return "lazy-Hello,World";
}
执行区别
- 及时执行
- 延迟执行
private static void level4() {
getMessage();//及时执行
Supplier<String> message = supplyMessage();//待执行状态
message.get();//实际执行
}
4.1.6 Spring 5.0+
public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
T getObject(Object... args) throws BeansException;
@Nullable
T getIfAvailable() throws BeansException;
default T getIfAvailable(Supplier<T> defaultSupplier) throws BeansException {
T dependency = getIfAvailable();
return (dependency != null ? dependency : defaultSupplier.get());
}
...
使用
ObjectProvider<User> beanProvider = applicationContext.getBeanProvider(User.class);
System.out.println(beanProvider.getIfAvailable(User::createUser));
运来这一步可能使用 beanFactory.getBean(User.class)
直接获取 Spring IoC 容器中的 User 对象,如果容器中没有就会报错;但是使用 beanProvider.getIfAvailable(User::createUser)
,相当于给了一个默认值(方法),如果容器没有的话就使用 User::createUser
来创建 User 对象,并返回。
4.2 Consumer
4.2.1 Consumer 接口定义
- 基本特点:只进不出
- 编程范式:作为方法/构造参数
- 使用场景:执行 Callback
4.2.2 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); };
}
}
普通版本
public static void level1() {
System.out.println("xwf");
}
Consumer 版本
public static void level2() {
Consumer<String> consumer = System.out::println;
consumer.accept("xwf");
}
自定义方法
public static void level3() {
Consumer<String> consumer = ConsumerDemo::echo;
consumer.accept("xwf");
}
public static void echo(String str) {
System.out.println("echo1:" + str);
}
Consumer#andThen
public static void displayAndThen() {
Consumer<String> consumer2 = ConsumerDemo::echo2;
Consumer<String> consumer3 = ConsumerDemo::echo3;
Consumer<String> consumer1 = ConsumerDemo::echo;
// Fluent API
consumer1.andThen(consumer2).andThen(consumer3).accept("xwf");
}
public static void echo(String str) {
System.out.println("echo1:" + str);
}
public static void echo2(String str) {
System.out.println("echo2:" + str);
}
public static void echo3(String str) {
System.out.println("echo3:" + str);
}
执行结果
echo1:xwf
echo2:xwf
echo3:xwf
4.2.3 Spring
public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
...
default void ifAvailable(Consumer<T> dependencyConsumer) throws BeansException {
T dependency = getIfAvailable();
if (dependency != null) {
dependencyConsumer.accept(dependency);
}
}
...
T 作为参数传递给 Consumer,然后来执行 beanProvider.ifAvailable(System.out::println);
4.3 Function
4.3.1 Function 接口定义
- 基本特点:有进有出
- 编程范式:作为方法/构造参数
- 使用场景:类型转换、业务处理等
4.3.2 源码
将类型 T 转化为类型 R
@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;
}
}
4.3.3 示例
public class FunctionalDemo {
public static void main(String[] args) {
displayCompose();
}
/**
* String -> Integer
*/
public static void level1() {
Function<String, Integer> function = Integer::valueOf;
Integer res = function.apply("1");
System.out.println(res);
}
/**
* Long -> String
*/
public static void level2() {
Function<Long, String> function = String::valueOf;
String res = function.apply(13L);
System.out.println(res);
}
/**
* compose
*/
public static void displayCompose() {
Function<String, Integer> function = Integer::valueOf;
// 1 -> "1" -> 1
Integer value = function.compose(String::valueOf).apply(1);
System.out.println(value);
}
}
典型应用
- java.util.stream.Stream
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
示例
class User {
private String name;
private Integer age;
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
public User() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public static List<User> getUsers() {
List<User> users = new ArrayList<>();
for (int i = 0; i < 10; i++) {
users.add(new User("person" + i, i));
}
return users;
}
}
将 User 集合转化成 User 的 name 集合,通过 map(User::getName)
。
private static void map() {
List<String> userNames = User.getUsers().stream().map(User::getName)
.collect(Collectors.toList());
System.out.println(userNames);
}
执行结果
[person0, person1, person2, person3, person4, person5, person6, person7, person8, person9]
4.3.5 Spring 类似接口
- org.springframework.core.convert.converter.Converter
public interface Converter<S, T> {
T convert(S source);
}
4.4 Predicate
4.4.1 Predicate 接口定义
- 基本特点:有进有出
- 编程范式:作为方法/构造参数
- 使用场景:类型转换、业务处理等
4.4.2 源码
@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);
}
}
4.4.3 示例
public class PredicateDemo {
public static void main(String[] args) {
Predicate<File> predicate = FileFilter::accpet;
System.out.println(predicate.test(new File("/aaa")));
}
static class FileFilter {
static boolean accpet(File path) {
return true;
}
}
}
典型应用
- java.util.stream.Stream
Stream<T> filter(Predicate<? super T> predicate);
4.4.4 设计
public class PredicateDesignDemo {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Collection<Integer> even = filter(numbers, PredicateDesignDemo::condition);
even.forEach(System.out::println);
}
private static boolean condition(Integer number) {
if (number % 2 == 0) {
return false;
} else {
return true;
}
}
private static <E> Collection<E> filter(Collection<E> source, Predicate<E> predicate) {
// 集合类操作,请不要直接利用参数
List<E> copy = new ArrayList<>(source);
Iterator<E> iterator = copy.iterator();
while ((iterator).hasNext()) {
E element = iterator.next();
if (predicate.test(element)) {
iterator.remove();
}
}
return Collections.unmodifiableList(copy);
}
}
以上代码等同于
List<Integer> collect = Stream.of(1, 2, 3, 4, 5).filter(num -> {
return num % 2 == 0;
}).collect(Collectors.toList());
4.5 Action
- 基本特点:不进不出
- 使用场景:线程执行
4.5.1 示例
public class ActionDemo {
public static void main(String[] args) {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("Hello,World");
}
};
// invokedynamic 指令 @since jdk 1.7
// java.lang.invoke.MethodHandle
// java.lang.invoke.InvokeDynamic
Runnable runnable2 = () -> {
System.out.println("Hello,World");
};
runnable.run();
}
}
5.总结
综上所述,函数式接口就是将我们一些特殊的接口方法进行了分类,根据方法参数和返回值来确定属于哪一类型
- 提供类型 - Supplier<T>
- 只出不进
- 消费类型 - Consumer<T>
- 只进不出
- 转换类型 - Function<T,R>
- 有进有出
- 断定类型 - Predicate<T>
- 有进有出,返回值为 Boolean 类型
- 隐藏类型 - Action
- 不进不出
示例
public class LambdaDemo {
public static void main(String[] args) {
// 只出不进
Supplier<String> supplier = LambdaDemo::getMessage;
// 只进不出
Consumer<String> consumer = LambdaDemo::printlnMessage;
// 有进有出
Function<Integer,String> function = LambdaDemo::integerToString;
// 有进有出+判断
Predicate<Integer> predicate = LambdaDemo::existNumber;
// Action 隐藏类型 不进不出
Runnable runnable = ()->{
};
}
public static String getMessage() {
return "xwf";
}
public static void printlnMessage(String msg) {
System.out.println(msg);
}
public static String integerToString(Integer number) {
return String.valueOf(number);
}
public static Boolean existNumber(Integer number) {
return true;
}
}
应用场景
- 函数式接口提供 lambda 表达式和方法引用的目标类型
- 函数式接口可以匹配或适配为 lambda 表达式的参数和返回类型
- 将函数式接口作为方法的参数来进行使用,增加程序设计的灵活性