Java8之函數(shù)式接口及常用函數(shù)式接口講解
函數(shù)式接口
1.概念
函數(shù)式接口(Functional Interface)就是一個(gè)有且僅有一個(gè)抽象方法,但是可以有多個(gè)非抽象方法的接口。
函數(shù)式接口可以被隱式轉(zhuǎn)換為 lambda 表達(dá)式。
Lambda 表達(dá)式和方法引用(實(shí)際上也可認(rèn)為是Lambda表達(dá)式)上。
2.@FunctionalInterface
@FunctionalInterface public interface Runnable { /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run(); }
我們常用的Runnable接口就是個(gè)典型的函數(shù)式接口,我們可以看到它有且僅有一個(gè)抽象方法run。并且可以看到一個(gè)注解@FunctionalInterface,這個(gè)注解的作用是強(qiáng)制你的接口只有一個(gè)抽象方法。
如果有多個(gè)話直接會(huì)報(bào)錯(cuò),如圖:
idea錯(cuò)誤提示:
編譯時(shí)錯(cuò)誤提示:
這里當(dāng)你寫了第二個(gè)方法時(shí),編譯就無(wú)法通過,idea甚至在編碼階段就行了提示。
3.函數(shù)式接口使用方式
我們直接上代碼,首先定義一個(gè)函數(shù)式接口
@FunctionalInterface public interface SingleAbstraMethodInterface { public abstract void singleMethod(); }
我們定一個(gè)test類,封裝一個(gè)方法,將SingleAbstraMethodInterface當(dāng)做參數(shù)傳入方法,并打印一句話
public class Test { public void testMethod(SingleAbstraMethodInterface single){ System.out.println("即將執(zhí)行函數(shù)式接口外部定義方法"); single.singleMethod(); } public static void main(String[] args) { Test test = new Test(); test.testMethod(new SingleAbstraMethodInterface() { @Override public void singleMethod() { System.out.println("執(zhí)行函數(shù)式接口定義方法"); } }); } }
執(zhí)行結(jié)果:
即將執(zhí)行函數(shù)式接口外部定義方法
執(zhí)行函數(shù)式接口定義方法
是不是和我們預(yù)期結(jié)果一樣。這個(gè)過程是不是有的同學(xué)已經(jīng)發(fā)現(xiàn),怎么這么像jdk里面的一些接口的使用,比如常用的Runnable接口。
我們來(lái)看看代碼。
public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { System.out.println("run方法執(zhí)行了"); } }).start(); }
再看下Runnable接口源碼,是不是一樣
@FunctionalInterface public interface Runnable { public abstract void run(); }
這時(shí),有的同學(xué)會(huì)說,我用Runnable接口可不是這么用的,我的寫法比你優(yōu)雅,我是這么寫的。
沒錯(cuò)函數(shù)式接口,函數(shù)式編程,我們可以使用lambda表達(dá)式的寫法,可以讓代碼更優(yōu)雅,具體可以參照我的前一篇帖子。Java8之Lambda表達(dá)式
public static void main(String[] args) { new Thread(()-> { System.out.println("run方法執(zhí)行了"); }).start(); }
常用函數(shù)式接口
1.JDK提供的函數(shù)式接口舉栗
java.lang.Runnable,
java.awt.event.ActionListener,
java.util.Comparator,
java.util.concurrent.Callable
java.util.function包下的接口,如Consumer、Predicate、Supplier等
其實(shí),jdk中給我們提供了很多的函數(shù)式接口,我們平時(shí)都會(huì)用到,只不過大家沒有注意到而已,這里我結(jié)合實(shí)際代碼講解幾個(gè)常用的函數(shù)式接口。想想大家平時(shí)常常用到stream流的各種方法來(lái)處理list。我看去stream類中看一下它提供的方法??梢钥吹接袔讉€(gè)出鏡率較高的函數(shù)式接口
Supplier
Comsumer
Predicate
Function
2.Supplier
@FunctionalInterface public interface Supplier<T> { /** * Gets a result. * @return a result */ T get(); }
Supplier接口的get方法沒有入?yún)?,返回一個(gè)泛型T對(duì)象。
我們來(lái)看幾個(gè)使用 的實(shí)例。
先寫一個(gè)實(shí)驗(yàn)對(duì)象girl,我們定義一個(gè)方法,創(chuàng)建對(duì)象,我們使用lambda的方式來(lái)調(diào)用并創(chuàng)建對(duì)象。
@Data @AllArgsConstructor @NoArgsConstructor public class Girl { private String name; private Double size; private Double price; public static Girl create(final Supplier<Girl> supplier) { return supplier.get(); } public static void main(String[] args) { //創(chuàng)建對(duì)象 Girl girl1 = Girl.create(Girl::new); Girl girl2 = Girl.create(()-> new Girl("lily",33d,1700d)); Girl girl = null; //orElseGet Girl girl3 = Optional.ofNullable(girl).orElseGet(Girl::new); } }
需要注意的是,我沒每調(diào)用一次get方法,都會(huì)重新創(chuàng)建一個(gè)對(duì)象。
orElseGet方法入?yún)⑼瑯右彩荢upplier,這里對(duì)girl實(shí)例進(jìn)行判斷,通過Supplier實(shí)現(xiàn)了懶加載,也就是說只有當(dāng)判斷girl為null時(shí),才會(huì)通過orElseGet方法創(chuàng)建新的對(duì)象并且返回。不得不說這個(gè)設(shè)計(jì)是非常的巧妙。
3.Consumer
源碼如下:
@FunctionalInterface public interface Consumer<T> { /** * Performs this operation on the given argument. * * @param t the input argument */ void accept(T t); /** * Returns a composed {@code Consumer} 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 Consumer} that performs in sequence this * operation followed by the {@code after} operation * @throws NullPointerException if {@code after} is null */ default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; } }
可以看到accept方法接受一個(gè)對(duì)象,沒有返回值。
那么我們來(lái)實(shí)戰(zhàn),首先使用Lambda表達(dá)式聲明一個(gè)Supplier的實(shí)例,它是用來(lái)創(chuàng)建Girl實(shí)例;再使用Lambda表達(dá)式聲明一個(gè)Consumer的實(shí)例,它是用于打印出Girl實(shí)例的toString信息;最后Consumer消費(fèi)了Supplier生產(chǎn)的Girl。
我們常用的forEach方法入?yún)⒁彩荂onsumer。
使用代碼如下:
Supplier<Girl> supplier = ()-> new Girl("lucy",33d,1700d); Consumer<Girl> consumer = (Girl g)->{ System.out.println(g.toString()); }; consumer.accept(supplier.get()); ArrayList<Integer> list = Lists.newArrayList(1, 2, 3, 4, 5); list.forEach(System.out::println);
4.Predicate
@FunctionalInterface public interface Predicate<T> { /** * Evaluates this predicate on the given argument. * * @param t the input argument * @return {@code true} if the input argument matches the predicate, * otherwise {@code false} */ boolean test(T t); }
可以看到test方法接受一個(gè)對(duì)象,返回boolean類型,這個(gè)函數(shù)顯然是用來(lái)判斷真假。
那么我們用這個(gè)接口來(lái)構(gòu)造一個(gè)判斷女孩子條件的示例,還有我們常用的stream流中,filter的入?yún)⒁彩荘redicate,代碼如下:
Supplier<Girl> supplier = ()-> new Girl("lucy",33d,1700d); Predicate<Girl> girl36d = (Girl g)-> Objects.equals(g.getSize(),36d); Predicate<Girl> girl33d = (Girl g)-> Objects.equals(g.getSize(),33d); boolean test33 = girl33d.test(supplier.get()); boolean test36 = girl36d.test(supplier.get()); System.out.println(supplier.get().getName() +"是否為[36d] :"+test36); System.out.println(supplier.get().getName() +"是否為[33d] :"+test33);
結(jié)果為:
lucy是否為[36d] :false
lucy是否為[33d] :true
ArrayList<Girl> list = Lists.newArrayList(); list.add(new Girl("露西", 33d, 2000d)); list.add(new Girl("格蕾絲", 36d, 3000d)); list.add(new Girl("安娜", 28d, 1500d)); list.add(new Girl("克瑞斯", 31d, 1800d)); Predicate<Girl> greaterThan30d = (Girl g)-> g.getSize()>=30d; list.stream().filter(greaterThan30d).forEach(System.out::println);
結(jié)果如下:
Girl(name=露西, size=33.0, price=2000.0)
Girl(name=格蕾絲, size=36.0, price=3000.0)
Girl(name=克瑞斯, size=31.0, price=1800.0)
5.Function
@FunctionalInterface public interface Function<T, R> { /** * Applies this function to the given argument. * * @param t the function argument * @return the function result */ R apply(T t); }
這個(gè)看到apply接口接收一個(gè)泛型為T的入?yún)?,返回一個(gè)泛型為R的返回值,所以它的用途和Supplier還是略有區(qū)別。還是一樣我們舉個(gè)栗子用代碼來(lái)說明:
Supplier<Girl> supplier = ()-> new Girl("lucy",33d,1700d); Function<Girl,String> girlMark = (Girl g)-> g.getName()+"的尺寸是"+g.getSize()+",每次能賺"+g.getPrice()+"。"; System.out.println(girlMark.apply(supplier.get()));
我們可以看到funtion接口接收Girl對(duì)象實(shí)例,對(duì)girl的成員變量進(jìn)行拼接,返回girl的描述信息。其基本用法就是如此。
lucy的尺寸是33.0,每次能賺1700.0。
6.常用函數(shù)式接口相關(guān)擴(kuò)展接口
這里我列舉一些Supplier相關(guān)接口
a.Supplier相關(guān)拓展接口
接口名稱 | 方法名稱 | 方法簽名 |
---|---|---|
Supplier | get | () -> T |
BooleanSupplier | getAsBoolean | () -> boolean |
DoubleSupplier | getAsDouble | () -> double |
IntSupplier | getAsInt | () -> int |
LongSupplier | getAsLong | () -> long |
b.Comsumer相關(guān)拓展接口
接口名稱 | 方法名稱 | 方法簽名 |
---|---|---|
Consumer | accept | (T) -> void |
DoubleConsumer | accept | (double) -> void |
IntConsumer | accept | (int) -> void |
LongConsumer | accept | (long) -> void |
ObjDoubleConsumer | accept | (T, double) -> vo |
ObjIntConsumer | accept | (T, int) -> void |
ObjLongConsumer | accept | (T, long) -> void |
c.Predicate相關(guān)拓展接口
接口名稱 | 方法名稱 | 方法簽名 |
---|---|---|
Predicate | test | (T) -> boolean |
BiPredicate | test | (T, U) -> boolean |
DoublePredicate | test | (double) -> bool |
IntPredicate | test | (int) -> boolean |
LongPredicate | test | (long) -> boolean |
d.Function相關(guān)的接口
接口名稱 | 方法名稱 | 方法簽名 |
---|---|---|
Function | apply | (T) -> R |
BiFunction | apply | (T, U) -> R |
DoubleFunction | apply | (double) -> R |
DoubleToIntFunction | applyAsInt | (double) -> int |
DoubleToLongFunction | applyAsLong | (double) -> long |
IntFunction | apply | (int) -> R |
IntToDoubleFunction | applyAsDouble | (int) -> double |
IntToLongFunction | applyAsLong | (int) -> long |
LongFunction | apply | (long) -> R |
LongToDoubleFunction | applyAsDouble | (long) -> double |
LongToIntFunction | applyAsInt | (long) -> int |
ToDoubleFunction | applyAsDouble | (T) -> double |
ToDoubleBiFunction | applyAsDouble | (T, U) -> double |
ToIntFunction | applyAsInt | (T) -> int |
ToIntBiFunction | applyAsInt | (T, U) -> int |
ToLongFunction | applyAsLong | (T) -> long |
ToLongBiFunction | applyAsLong | (T, U) -> long |
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Springboot mybatis-plus配置及用法詳解
這篇文章主要介紹了Springboot mybatis-plus配置及用法詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09sonar-scanner連接sonarquebe7的sonar.java.binaries問題的解決方案
今天小編就為大家分享一篇關(guān)于sonar-scanner連接sonarquebe7的sonar.java.binaries問題的解決方案,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12教你使用eclipse?搭建Swt?環(huán)境的全過程
本文給大家分享使用eclipse?搭建Swt?環(huán)境的全過程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-12-12Spring Data MongoDB 數(shù)據(jù)庫(kù)批量操作的方法
在項(xiàng)目開發(fā)中經(jīng)常會(huì)批量插入數(shù)據(jù)和更新數(shù)據(jù)的操作,這篇文章主要介紹了Spring Data MongoDB 數(shù)據(jù)庫(kù)批量操作的方法,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2018-11-11SpringAop @Around執(zhí)行兩次的原因及解決
這篇文章主要介紹了SpringAop @Around執(zhí)行兩次的原因及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07