Java8 Function、Consumer、Supplier
有關JDK8新特性之前寫了三篇博客:
這一篇我們來了解JDK8已經定義好的幾個函數式接口。
一、概述
Jdk8之后新增的一個重要的包 : java.util.function
該包下所有的接口都是函數式接口, 按分類主要分為四大接口類型: Function
、Consumer
、Predicate
、Supplier
。有關Predicate這里不再講解,因為上面有單獨寫過一篇博客。

延伸如下

這里也僅僅是展示一部分,我們看看java.util.function包下

二、Consumer
作用
一聽這名字就知道是消費某個對象,沒有返回值。
1、源碼
在源碼中只有兩個方法,一個抽象方法,一個默認方法。
@FunctionalInterface
public interface Consumer<T> {
/**
* 抽象方法:傳入一個指定泛型的參數,無返回值
*/
void accept(T t);
/**
* 如同方法名字一樣andThen,類似一種相加的功能(下面會舉例說明)
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
2、使用示例
public static void main(String[] args) {
testConsumer();
testAndThen();
}
/**
* 一個簡單的平方計算
*/
public static void testConsumer() {
//設置好Consumer實現方法
Consumer<Integer> square = x -> System.out.println("平方計算 : " + x * x);
//傳入值
square.accept(2);
}
/**
* 定義3個Consumer並按順序進行調用andThen方法
*/
public static void testAndThen() {
//當前值
Consumer<Integer> consumer1 = x -> System.out.println("當前值 : " + x);
//相加
Consumer<Integer> consumer2 = x -> { System.out.println("相加 : " + (x + x)); };
//相乘
Consumer<Integer> consumer3 = x -> System.out.println("相乘 : " + x * x);
//andThen拼接
consumer1.andThen(consumer2).andThen(consumer3).accept(1);
}
運行結果

單個這樣消費看去並沒啥意義,但如果是集合操作就有意義了,所以Jdk8的Iterator接口就引入了Consumer。
3、JDK8使用
Iterable接口的forEach方法需要傳入Consumer,大部分集合類都實現了該接口,用於返回Iterator對象進行迭代。
public interface Iterable<T> {
//forEach方法傳入的就是Consumer
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
}
我們在看給我們帶來的便利
public static void main(String[] args) {
//假設這里有個集合,集合里的對象有個status屬性,現在我想對這個屬性賦值一個固定值
List<Pension> pensionList = new ArrayList<>();
//1、傳統的通過for循環添加
for (Pension pension : pensionList) {
pension.setStatus(1);
}
//2、通過forEach的Consumer添加
pensionList.forEach(x -> x.setStatus(1));
}
這樣一比較是不是代碼簡潔了點,這就是Consumer是我們代碼帶來簡潔的地方。
三、Supplier
作用
提前定義可能返回的一個指定類型結果,等需要調用的時候再獲取結果。
1、源碼
@FunctionalInterface
public interface Supplier<T> {
/**
* 只有這一個抽象類
*/
T get();
}
源碼非常簡單。
2、JDK8使用
在JDK8中Optional對象有使用到
Optional.orElseGet(Supplier<? extends T>) //當this對象為null,就通過傳入supplier創建一個T返回。
我們看下源碼
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
使用示例
public static void main(String[] args) {
Person son = null;
//先判斷son是否為null,如果為不為null則返回當前對象,如果為null則返回新創建的對象
BrandDTO optional = Optional.ofNullable(son).orElseGet(() -> new Person());
}
這樣代碼是不是又簡單了。有關Optional這里就不多說,接下來會單獨寫一篇博客。
四、Function
作用
實現一個”一元函數“,即傳入一個值經過函數的計算返回另一個值。
1、源碼
@FunctionalInterface
public interface Function<T, R> {
/**
* 抽象方法: 根據一個數據類型T加工得到一個數據類型R
*/
R apply(T t);
/**
* 組合函數,調用當前function之前調用
*/
default <V> java.util.function.Function<V, R> compose(java.util.function.Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
/**
* 組合函數,調用當前function之后調用
*/
default <V> java.util.function.Function<T, V> andThen(java.util.function.Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
/**
* 靜態方法,返回與原函數參數一致的結果。x=y
*/
static <T> java.util.function.Function<T, T> identity() {
return t -> t;
}
}
2、使用示例
public static void main(String[] args) {
applyTest();
andThenTest();
composeTest();
test();
}
/**
* 1、apply 示例
*/
private static void applyTest() {
//示例1:定義一個funciton,實現將String轉換為Integer
Function<String, Integer> function = x -> Integer.parseInt(x);
Integer a = function.apply("100");
System.out.println(a.getClass());
// 結果:class java.lang.Integer
}
/**
* 2、andThen 示例
*/
private static void andThenTest() {
//示例2:使用andThen() 實現一個函數 y=10x + 10;
//先執行 10 * x
Function<Integer, Integer> function2 = x -> 10 * x;
//通過andThen在執行 這里的x就等於上面的10 * x的值
function2 = function2.andThen(x -> x + 10);
System.out.println(function2.apply(2));
//結果:30
}
/**
* 3、compose 示例
*/
private static void composeTest() {
//示例3:使用compose() 實現一個函數 y=(10+x)2;
Function<Integer, Integer> function3 = x -> x * 2;
//先執行 x+10 在執行(x+10)*2順序與上面相反
function3 = function3.compose(x -> x + 10);
System.out.println(function3.apply(3));
//結果:26
}
/**
* 4、綜合示例
*/
private static void test() {
//示例4:使用使用compose()、andThen()實現一個函數 y=(10+x)*2+10;
//執行第二步
Function<Integer, Integer> function4 = x -> x * 2;
//執行第一步
function4 = function4.compose(x -> x + 10);
//執行第三步
function4 = function4.andThen(x -> x + 10);
System.out.println(function4.apply(3));
//結果:36
}
3、JDK8使用
有兩個地方很常用
1、V HashMap.computeIfAbsent(K , Function<K, V>) // 簡化代碼,如果指定的鍵尚未與值關聯或與null關聯,使用函數返回值替換。
2、<R> Stream<R> map(Function<? super T, ? extends R> mapper); // 轉換流
computeIfAbsent使用示例
Map<String, List<String>> map = new HashMap<>();
List<String> list;
// java8之前寫法
list = map.get("key");
if (list == null) {
list = new LinkedList<>();
map.put("key", list);
}
list.add("11");
// 使用 computeIfAbsent 可以這樣寫 如果key返回部位空則返回該集合 ,為空則創建集合后返回
list = map.computeIfAbsent("key", k -> new ArrayList<>());
list.add("11");
stream中map使用示例
public static void main(String[] args) {
List<Person> persionList = new ArrayList<Person>();
persionList.add(new Person(1,"張三","男",38));
persionList.add(new Person(2,"小小","女",2));
persionList.add(new Person(3,"李四","男",65));
//1、只取出該集合中所有姓名組成一個新集合(將Person對象轉為String對象)
List<String> nameList=persionList.stream().map(Person::getName).collect(Collectors.toList());
System.out.println(nameList.toString());
}
代碼是不是又簡潔了。
總結
這些函數式接口作用在我看來,就是定義一個方法,方法中有個參數是函數式接口,這樣的話函數的具體實現則由調用者來實現。這就是函數式接口的意義所在。
一般我們也會很少去定義一個方法,方法參數包含函數接口。我們更重要的是學會使用JDk8中帶有函數式接口參數的方法,來簡化我們的代碼。
參考
1、JDK1.8函數式接口Function、Consumer、Predicate、Supplier
你如果願意有所作為,就必須有始有終。(25)