网站首页 > 技术文章 正文
目录
一、什么是函数式接口?
二、函数式接口应用实战
1. BiConsumer接口和Consumer接口
1) accept(T t,U u)方法
2) andThen(BiConsumer)方法
2. Supplier接口
3. Supplier接口和Consumer接口结合应用实战
1) 定义of()方法创建类的对象
2) 定义with()回调属性赋值
3) 定义build()方法开始回调
4) 测试
5) 调用过程解读
6) 完整代码
函数式编程是JDK1.8的新特性,函数式接口是函数式编程主要的体现,在有些情况下我们可以用lambda表达式替代函数式接口传参。
首先使用hashmap的computeIfAbsent方法举个栗子,该方法的作用是从hashmap中根据key获取一个值,如果没有值, 那么就给该key填充一个值。
Map<String, Integer> userMap = new HashMap<>();
Integer value = userMap.computeIfAbsent("1", new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return 1;
}
});
我们可以看到Funtion接口以参数的形式传给了computeIfAbsent方法,在apply()方法里Return了1, 得到value的值为1,因为computeIfAbsent方法在get一个不存在的key时,会通过接口对象来调用apply()方法,然后将apply()方法的结果返回并将key和value存入到map里。
default V computeIfAbsent(K key,
Function<? super K, ? extends V> mappingFunction) {
Objects.requireNonNull(mappingFunction);
V v;
if ((v = get(key)) == null) {
V newValue;
if ((newValue = mappingFunction.apply(key)) != null) {
put(key, newValue);
return newValue;
}
}
return v;
}
使用lambda表达式替换,结果是一样的:
package func;
import java.util.HashMap;
import java.util.Map;
public class FunctionTests {
public static void main(String[] args) {
Map<String, Integer> userMap = new HashMap<>();
Integer value = userMap.computeIfAbsent("1", s -> 1);
if (userMap.get("1").equals(value)) {
System.out.println("yes!");
}
}
}
lamda表达式看起来更简洁一点。
一、什么是函数式接口?
函数式接口是java8中新加入的一批接口,他们由@FutionalInterface注解标记的接口,我们可以在rt.jar包下的java.util.function包里找到有很多函数式接口,比如常用的函数式接口有Function、Supplier、consumer和Prdicate等。
可以发现,函数式接口里只允许一个抽象方法,我们也可以通过@FunctionalInterface注解自定义一个函数式接口。
@FunctionalInterface
public interface Function0<T> {
T get();
}
如果包含多个抽象方法,那么会出现报错的提示:
二、函数式接口应用实战
1. BiConsumer接口和Consumer接口
BiConsumer定义了两个泛型类型T和R,分别做为accept()方法的参数类型,BiConsumer支持2个参数。
@FunctionalInterface
public interface BiConsumer<T, U> {
/**
* Performs this operation on the given arguments.
*
* @param t the first input argument
* @param u the second input argument
*/
void accept(T t, U u);
/**
* Returns a composed {@code BiConsumer} that performs, in sequence, this
* operation followed by the {@code after} operation. If performing either
* operation throws an exception, it is relayed to the caller of the
* composed operation. If performing this operation throws an exception,
* the {@code after} operation will not be performed.
*
* @param after the operation to perform after this operation
* @return a composed {@code BiConsumer} that performs in sequence this
* operation followed by the {@code after} operation
* @throws NullPointerException if {@code after} is null
*/
default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after) {
Objects.requireNonNull(after);
return (l, r) -> {
accept(l, r);
after.accept(l, r);
};
}
}
1) accept(T t,U u)方法
accept方法能接受2个类型的参数,例如我们可以在accept方法里写一些逻辑,利用参数t和u做一些事情:
package java8.func;
import java.util.function.BiConsumer;
/**
* 测试java.uti.function.BiConsumer接口
*/
public class BiConsumerTests {
static class Record<T, R> {
private final BiConsumer<T, R> biConsumer;
// 初始化biConsumer
public Record(BiConsumer<T, R> biConsumer) {
this.biConsumer = biConsumer;
}
// 调用accept方法
public void consume(T t, R r) {
biConsumer.accept(t, r);
}
private Integer value;
public Integer getValue() {
return value;
}
public void setValue(Integer value) {
this.value = value;
}
}
public static void main(String[] args) {
Record<String, Integer> record = new Record<>((k, v) -> {
System.out.println(k+","+v);
});
record.consume("1", 1);
}
}
2) andThen(BiConsumer<? super T,? super U>)方法
<? super T, ? super U> super表示下限的类型,extends 表示上限的类型,参数1 至少为T类型或者为T类型的父类,参数2为U类型或者U类型的父类。andThen方法表示会继承当前BiConsumer继续调用下一个BiConsumer里的accept()方法。
下面使用andThen 结合accept实现一个广播功能,一次Accept,多个注册的consumer都能收到消息。
package java8.func;
import java.util.function.BiConsumer;
/**
* 测试java.uti.function.BiConsumer接口
*/
public class BiConsumerTests {
static class Record<T, R> {
private final BiConsumer<T, R> biConsumer;
private Record<T, R> next;
// 初始化biConsumer
public Record(BiConsumer<T, R> biConsumer) {
this.biConsumer = biConsumer;
}
// 调用accept方法,多个biConsumer
public void consume(T t, R r) {
//当前
BiConsumer<T, R> chainConsumer = this.biConsumer;
while (this.next != null) {
chainConsumer = chainConsumer.andThen(this.next.biConsumer);
next = next.next;
}
chainConsumer.accept(t, r);
}
public void addLast(BiConsumer<T, R> biConsumer) {
// 遍历链表到表尾
Record<T,R> current=this;
while (current.next != null) {
current = current.next;
}
current.next = new Record<>(biConsumer);
}
private Integer value;
public Integer getValue() {
return value;
}
public void setValue(Integer value) {
this.value = value;
}
}
public static void main(String[] args) {
Record<String, Integer> record = new Record<>((k, v) -> {
System.out.println(k + "," + v);
});
record.addLast((k, v) -> {
System.out.println(k + "-" + v);
});
record.addLast((k, v) -> {
System.out.println(k + "+" + v);
});
record.consume("1", 1);
}
}
打印结果:
BiConsumer和Consumer区别在于Accept方法入参的个数,consumer里的accept方法只支持一个T类型的参数。
2. Supplier接口
Supplier接口比consumer接口要更简单点,只有一个get()方法,该方法没有任何参数,也没有andThen,可以通过Supplier给一个类里的属性进行初始化,如下通过supplier给一个List<T>进行初始化。
package java8.func;
import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;
public class SupplierTests {
static class Record<T> {
private final Supplier<List<T>> supplier;
public Record(Supplier<List<T>> supplier) {
this.supplier = supplier;
}
public List<T> getAllRecords() {
return supplier.get();
}
}
public static void main(String[] args) {
Record<Integer> record = new Record<>(() -> Arrays.asList(1, 2, 3, 4, 5));
List<Integer> records = record.getAllRecords();
System.out.println(records);
}
}
打印结果:
3. Supplier接口和Consumer接口结合应用实战
我们可以利用Supplier和Consumer接口编写一个类的初始化工具类,用一行代码去替代不断的set属性。
1) 定义of()方法创建类的对象
通过supplier将类的对象给保存下来。
public static <T> GenericBuilder<T> of(Supplier<T> type) {
return new GenericBuilder<>(type);
}
2) 定义with()回调属性赋值
这里用一个List去保存所有的with,就是说类的属性有多个赋值。
// T 为类的对象,U为值的属性的
public <U> GenericBuilder<T> with(BiConsumer<T, U> biConsumer, U value) {
//这一行代码为什么要这样写? 因为我们可以先将用accept方法将Consumer放入到typeModifier列表里
//当 调用tConsumer.accept(typeInstance)时, typeInstance对象会传入到每一个consumer的accept方法里
// 调用了biConsumer.accept(t,value)方法,就相当于调用了user.setUserName("bing"), user.setPassWord(bing)方法
// Consumer<T> tConsumer =new Consumer<T>() {
// @Override
// public void accept(T t) {
// biConsumer.accept(t,value);
// }
// };
Consumer<T> tConsumer = t -> biConsumer.accept(t, value);
this.typeModifier.add(tConsumer);
return this;
}
3) 定义build()方法开始回调
因为我们用set方法去赋值时,需要用到的是同一个对象,因此我们可以把supplier传入给tConsumer.accept()方法。
public T build() {
T typeInstance = this.type.get();
// 给所有的consumer
this.typeModifier.forEach(tConsumer -> {
tConsumer.accept(typeInstance);
});
this.typeModifier.clear();
return typeInstance;
}
4) 测试
package java8.func;
import java.util.function.BiConsumer;
public class TypeBuilderTest {
static class User {
private Integer id;
private String userName;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", userName='" + userName + '\'' +
'}';
}
}
public static void main(String[] args) {
// User user = TypeBuilder.of(User::new)
// .with(User::setId, 1)
// .with(User::setUserName, "bing")
// .build();
User user = TypeBuilder.of(User::new)
.with(new BiConsumer<User, Integer>() {
@Override
public void accept(User user, Integer integer) {
user.setId(integer);
}
}, 1)
.with(User::setUserName, "bing")
.build();
System.out.println(user);
}
}
打印结果:
可以发现我们只需要不停地with就可以实现频繁的with赋值了,是不是变得更简洁了!
5)调用过程解读
6) 完整代码
package java8.func;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
public class TypeBuilder<T> {
private final Supplier<T> type;
private List<Consumer<T>> typeModifierLists = new ArrayList<>();
public TypeBuilder(Supplier<T> type) {
this.type = type;
}
public static <T> TypeBuilder<T> of(Supplier<T> type) {
return new TypeBuilder<T>(type);
}
//将属性设置组装成Consumer。
public <U> TypeBuilder<T> with(BiConsumer<T, U> biConsumer, U value) {
// Consumer<T> consumer = t -> biConsumer.accept(t, value);
Consumer<T> consumer = new Consumer<T>() {
@Override
public void accept(T t) {
// 回调目标方法
biConsumer.accept(t, value);
}
};
this.typeModifierLists.add(consumer);
return this;
}
public T build() {
T type = this.type.get();
// 将type传给所有的consumer, consumer接收到type后,执行biConsumer.accept(t,value), 然后回调setUserName。
// this.typeModifierLists.forEach(tConsumer -> tConsumer.accept(type));
this.typeModifierLists.forEach(new Consumer<Consumer<T>>() {
@Override
public void accept(Consumer<T> tConsumer) {
tConsumer.accept(type);
}
});
this.typeModifierLists.clear();
return type;
}
}
猜你喜欢
- 2024-11-16 3.1 Python高级编程-函数式编程工具
- 2024-11-16 面向对象编程的一些思考(面向对象编程的理解)
- 2024-11-16 知识总结-Java8 Stream函数式编程
- 2024-11-16 bind、call、apply 区别?如何实现一个bind?
- 2024-11-16 Javascript基础重拾笔记之手写apply、call
- 2024-11-16 java 8新特性 常用内置函数式接口
- 2024-11-16 7、JavaScript 内置的常用对象有哪些?该对象常用的方法(必会)
- 2024-11-16 Java中“::”是什么含义(java中+是什么)
- 2024-11-16 Java 8 中的 Function:让代码从繁琐到简洁的魔法工具
- 2024-11-16 13万字详细分析JDK中Stream的实现原理(中)
- 标签列表
-
- content-disposition (47)
- nth-child (56)
- math.pow (44)
- 原型和原型链 (63)
- canvas mdn (36)
- css @media (49)
- promise mdn (39)
- readasdataurl (52)
- if-modified-since (49)
- css ::after (50)
- border-image-slice (40)
- flex mdn (37)
- .join (41)
- function.apply (60)
- input type number (64)
- weakmap (62)
- js arguments (45)
- js delete方法 (61)
- blob type (44)
- math.max.apply (51)
- js (44)
- firefox 3 (47)
- cssbox-sizing (52)
- js删除 (49)
- js for continue (56)
- 最新留言
-