今天將介紹Java另外兩個函數編程接口Consumer、Function,這兩個函數是幹嘛的呢?先看看官方的定義:
- Consumer:表示接受單個輸入參數但不返回結果的操作。
- Function:表示接受一個參數並生成結果的函數。
一、Consumer
1.1 源代碼
@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); };
}
}
1.2 混臉熟
其實Consumer我們經常使用,你看下面這個例子:
List<String> list = Arrays.asList("1", "2", "3");
list.forEach(System.out::println);
我們經常使用的forEach函數其實就是通過Consumer來實現的,所以掌握Consumer很有必要哦,下面看看forEach在ArrayList中的實現:
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int expectedModCount = modCount;
final E[] elementData = (E[]) this.elementData;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
action.accept(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
不用介紹,想必大家也能看的懂,Consumer就表示一個自定義的操作,將該操作作爲參數傳入到另一個函數內,可在該函數內執行自定義的操作。上面的for循環代碼就等同於:
for (int i=0; modCount == expectedModCount && i < size; i++) {
System.out.println(elementData[i]);
}
1.3 實現
如果操作比較常用或者通用,可以使用一個類去實現Consumer,保存該操作,在必要的時候能快速使用。
// 實體類
public class Person {
private String name;
private Integer age;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
//...
}
// 打印小孩
public class PrintChild implements Consumer<Person> {
@Override
public void accept(Person person) {
if (person.getAge() < 18) {
System.out.println(person.getName() + "還是個孩子啊");
}
}
}
// 測試代碼
List<Person> list1 = Arrays.asList(
new Person("大壯", 19),
new Person("小光", 16),
new Person("小小", 15));
list1.forEach(new PrintChild());
1.4 andThen函數介紹
函數源碼:
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
andThen函數的功能就是將兩個Consumer操作合併,並返回一個新的Consumer,使用如下:
// 打印年齡
public class PrintAge implements Consumer<Person> {
@Override
public void accept(Person person) {
System.out.println(person.getName() + "的年齡是:" + person.getAge() + "歲");
}
}
// 測試代碼
List<Person> list1 = Arrays.asList(
new Person("小雨", 19),
new Person("小光", 16),
new Person("小小", 15));
list1.forEach(new PrintChild().andThen(new PrintAge()));
// 結果
小雨的年齡是:19歲
小光是個孩子
小光的年齡是:16歲
小小是個孩子
小小的年齡是:15歲
1.5 Consumer的其他變體
接口名 | 參數 | 返回類型 | 描述 |
---|---|---|---|
BiConsumer | (T, U) | void | BiConsumer接受兩個參數 |
DoubleConsumer | double | void | 接受一個double類型的參數 |
IntConsumer | int | void | 接受一個int類型的參數 |
LongConsumer | long | void | 接受一個long類型的參數 |
ObjDoubleConsumer | (T, double) | void | 接受一個Object類型和一個double類型參數 |
ObjIntConsumer | (T, int) | void | 接受一個Object類型和一個int類型參數 |
ObjLongConsumer | (T, long) | void | 接受一個Object類型和一個long類型參數 |
二、Function
Function和Consumer在功能上是一致的,但是Function有返回結果。
2.1 源代碼
@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;
}
}
2.2 混臉熟
Function我們也是會經常使用到的,喏:
List<Person> list1 = Arrays.asList(
new Person("小雨", 19),
new Person("小光", 16),
new Person("小小", 15));
List<String> list = list1.stream()
.map(Person::getName)
.collect(Collectors.toList());
如果你看過筆者寫的另一篇文章(重識Java8函數式編程),那麼下面的代碼你應該也能看得懂,其實上面的map操作可以還原成:
List<String> list = list1.stream()
.map(person -> {
return person.getName();
})
.collect(Collectors.toList());
map內就是封裝了一個有返回值的函數,將函數作爲參數參入map內。
2.3 compose函數介紹
函數源碼:
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
該函數作用就是組合兩個Function,參數Function before先執行,執行後的結果交由調用方Function執行。
// 加法
public class AddFunc implements Function<Integer, Integer> {
private int origin;
public AddFunc(int origin) {
this.origin = origin;
}
@Override
public Integer apply(Integer integer) {
return this.origin + integer;
}
}
// 減法
public class ReduceFunc implements Function<Integer, Integer> {
private int origin;
private boolean isMinuend;// origin被減數與否
public ReduceFunc(int origin, boolean isMinuend) {
this.origin = origin;
this.isMinuend = isMinuend;
}
@Override
public Integer apply(Integer integer) {
return isMinuend ? this.origin - integer : integer - this.origin;
}
}
// 測試代碼
public class Test {
public static void main(String[] args) {
// 計算 1 + (2 - 3)
System.out.println(handle(new AddFunc(1).compose(new ReduceFunc(2, true)), 3));
}
public static int handle(Function<Integer, Integer> function, Integer integer) {
return function.apply(integer);
}
}
2.4 andThen函數介紹
函數源碼:
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
andThen函數的作用也是組合兩個Function,只不過參數Function後執行。
// 測試代碼
public class Test {
public static void main(String[] args) {
// 計算 1 + 2 - 4
System.out.println(handle(new AddFunc(1).andThen(new ReduceFunc(4, false)), 2));
}
public static int handle(Function<Integer, Integer> function, Integer integer) {
return function.apply(integer);
}
}
2.5 identity函數
源碼:
static <T> Function<T, T> identity() {
return t -> t;
}
好吧,這個函數的功能就是返回一個輸入和輸出都一樣的Function,那麼這個函數有什麼用呢?看示例:
// 示例:將list轉變爲map
List<Person> list = Arrays.asList(
new Person("xiaoxiao", 11),
new Person("dazhuang", 15));
Map<String, Person> map = list.stream()
.collect(Collectors.toMap(Person::getName, person -> person));
// 使用identity函數
List<Person> list = Arrays.asList(
new Person("xiaoxiao", 11),
new Person("dazhuang", 15));
Map<String, Person> map = list.stream()
.collect(Collectors.toMap(Person::getName, Function.identity()));
兩種使用方式都是可以的,但是你覺得哪個方式更有魅(bi)力(ge)呢?
2.6 我們能做啥
- 掌握了Function,在編寫公共組件邏輯的時候,你可以將部分邏輯上拋,由調用者去實現自己的特性,能增強組件的靈活性。
- 實現Function接口,定義一些常用操作,減少代碼的冗餘。
運用的基礎是先掌握,只有掌握才能熟練運用,乾了這杯毒雞湯。(小聲BB:端午最後一天假了,T . T)
2.7 Function的其他變體
接口名 | 參數 | 返回類型 | 描述 |
---|---|---|---|
BiFunction | (T, U) | R | BiFunction接受兩個參數 |
DoubleFunction | double | R | 接受一個double類型的參數 |
DoubleToIntFunction | double | int | 接受double類型參數,返回int類型結果 |
DoubleToLongFunction | double | long | 接受double類型參數,返回long類型結果 |
IntFunction | int | R | 接受一個int類型的參數 |
IntToDoubleFunction | int | double | 接受int類型參數,返回double類型結果 |
IntToLongFunction | int | long | 接受int類型參數,返回long類型結果 |
LongFunction | long | R | 接受一個long類型的參數 |
LongToDoubleFunction | long | double | 接受long類型參數,返回double類型結果 |
LongToIntFunction | long | int | 接受long類型參數,返回int類型結果 |
ToDoubleBiFunction | (T, U) | double | 接受兩個參數,返回double類型結果 |
ToDoubleFunction | T | double | 接受一個Object類型,返回double類型參數 |
ToIntBiFunction | (T, U) | int | 接受兩個參數,返回int類型結果 |
ToIntFunction | T | int | 接受一個Object類型,返回int類型參數 |
ToLongBiFunction | (T, U) | long | 接受兩個參數,返回long類型結果 |
ToLongFunction | T | long | 接受一個Object類型,返回long類型參數 |
文章推薦: