?技術的升級往往不是獨立的,而是一次系統性的升級,小部分升級通常是改BUG,JDK8的升級意義非常重大,各個升級環環相扣!本篇介紹的函數式接口和上篇講解的《??Lambda表達式??》緊密相關!本篇你只需要搞懂什么是函數式接口這個概念就行啦,代碼寫不寫無所謂,非常簡單!
掌握內容
- 函數式接口概念和意義
- 認識JDK內置函數式接口
- 函數式接口配合Lambda實現
- 自定義函數式接口
- @FunctionalInterface注解作用
函數式接口
函數式接口就是一個有且僅有一個抽象方法,但是可以有多個非抽象方法的接口,是Lambda表達式的實現前提,可以使用@FunctionalInterface注解修飾

函數式接口意義
Java一直倡導面向對象,隨著Python、Scala等語言的興起和新技術的挑戰,Java必須調整來支持更加廣泛的技術要求,所以Java不單單OOP【面向對象編程】同樣支持OOF【面向函數編程】。
以往需要通過匿名內部類實現,現在都可以通過Lambda實現,其實Lambda表達式可以看做是一個函數式接口的實例。
JDK內置函數式接口
注:不需要掌握,不需要掌握!我看網上很多資料都只寫了四個內置接口,比較局限,這里對JDK內置接口做一個全面的說明,只需要知道有這么多內置接口,并不是只有四個就可以了。
JDK8新增函數式接口:
JDK8新推出的函數式接口在java.util.function包下

作用如下:
函數式接口 | 參數類型 | 返回類型 | 用途 |
Consumer 消費型接口 | T | void | 對類型為T的對象應用操作,包含方法:void accept(T t) |
Supplier 供給型接口 | 無 | T | 返回類型為T的對象,包含方法:T get() |
Function<T, R>函數型接口 | T | R | 對類型為T的對象應用操作,并返回結果。結果是R類型的對象。包含方法:R apply(T t) |
Predicate斷定型接口 | T | boolean | 確定類型為T的對象是否滿足某約束,并返回boolean 值。包含方法:boolean test(T t) |
BiFunction<T,U,R> | T, U | R | 對類型為T,U參數應用操作,返回R類型的結果。包含方法為:Rapply(T t,U u) |
UnaryOperator(Function子接口) | T | T | 對類型為T的對象進行一元運算,并返回T類型的結果。包含方法為:Tapply(T t); |
BinaryOperator(BiFunction子接口) | T,T | T | 對類型為T的對象進行二元運算,并返回T類型的結果。包含方法為:Tapply(T t1,T t2); |
BiConsumer<T,U> | T,U | void | 對類型為T,U參數應用操作。包含方法為:voidaccept(Tt,Uu) |
BiPredicate<T,U> | T,U | boolean | 包含方法為:booleantest(Tt,Uu) |
ToIntFunction | T | int | 計算int值的函數 |
ToLongFunction | T | long | 計算long值的函數 |
ToDoubleFunction | T | double | 計算double值的函數 |
IntFunction | int | R | 參數為int類型的函數 |
LongFunction | long | R | 參數為long類型的函數 |
DoubleFunction | double | R | 參數為double類型的函數 |
JDK8之前的函數式接口:
JDK8之前也存在函數式接口,在JDK8升級之后這些接口頭部都加上了@FunctionalInterface修飾,如下:
- java.lang.Runnable【熟悉吧,創建線程】
- java.util.concurrent.Callable【創建線程】
- java.security.PrivilegedAction【執行計算】
- java.util.Comparator【Lambda一篇說過的 比較器】
- java.io.FileFilter【文件過濾器】
- java.nio.file.PathMatcher【路徑匹配】
- java.lang.reflect.InvocationHandler【動態代理】
- java.beans.PropertyChangeListener【屬性變化監聽器】
- java.awt.event.ActionListener【事件監聽器】
- javax.swing.event.ChangeListener【change事件監聽】
函數式接口使用
在上篇中我們已經使用過Runnable、Consumer、Supplier、Comparator等接口,這里我們再使用Function和Predicate接口,其他接口如果用到了可以照葫蘆畫瓢即可!
Function接口
接口定義如下:
小貼士:接口中有且僅有一個抽象方法的接口就是一個函數式接口,和默認實現以及靜態方法無關。
package java.util.function;
import java.util.Objects;
/**
*
* @param <T> 輸入參數類型
* @param
@FunctionalInterface
public interface Function<T, R> {
/**
抽象方法:輸入T類型參數,返回R類型的值
T和R是泛型哦,小伙伴不要搞混
*/
R apply(T t);
/**
JDK8新特性,接口中可以存在默認實現
*/
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));
}
/**
JDK8接口新特性:可以有靜態方法
*/
static <T> Function<T, T> identity(){
return t -> t;
}
}
接口特點:有一個輸入參數和一個輸出參數,也就是一進一出,如果你有需求是傳入一個參數并返回一個參數的需求可以使用該接口實現。
需求:
實現一個字符串轉換功能,將輸入的英文字符都轉換為大寫返回。
分析:
輸入和輸出數據都是字符串所有泛型類型均為String。
調用 apply方法進行計算之后接收返回值。
代碼實現:
public class FunctionMain {
public static void main(String[] args) {
// 1、原始匿名內部類寫法
Function<String, String> function1 = new Function<String, String>() {
@Override
public String apply(String inputStr) {
// 轉換為大寫
return inputStr.toUpperCase();
}
};
String result = function1.apply("Just give me a chance to start!");
System.out.println(result);
// 2、Lambda表達式寫法
Function<String,String> function2 = inputStr -> inputStr.toUpperCase();
String lambdaResult = function2.apply("Lambda really smells good!");
System.out.println(lambdaResult);
}
}
Predicate接口
接口定義:
該接口也存在默認實現和靜態方法,但是只有一個抽象方法,所以也是一個函數式接口。
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Predicate<T> {
/**
根據參數輸入判斷是否正確,返回true或者false
*/
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);
}
}
接口特點:該接口根據傳入數據通過計算之后返回true或者false,如果你想要做單個參數的判斷可以使用該接口。
小貼士:Java中有兩個Predicate類,不要導錯包,認準java.util.function包,當然自定義的類也不要起這個名字,【有許多初學者喜歡起同名的類】。

需求:判斷輸入的數據是否大于0。
分析:
- 泛型定義為Integer類型。
- 通過判斷返回結果即可。
代碼實現:
public class PredicateMain {
public static void main(String[] args){
// 1、原始實現方式
Predicate predicate1 = new Predicate<Integer>() {
@Override
public boolean test(Integer num){
return num > 0;
}
};
// 調用test方法
boolean result = predicate1.test(1024);
System.out.println(result);
// 2、Lambda表達式實現
Predicate<Integer> predicate2 = num -> num > 0;
// 調用test方法
boolean lambdaResult = predicate2.test(-1024);
System.out.println(lambdaResult);
}
}
小貼士:這些默認方法的接口,使用時不要調用錯方法就行!
自定義函數式接口
分析:
函數式接口就是有且僅有一個抽象方法,默認實現和靜態方法不影響它是一個函數式接口【JDK8支持接口有默認方法和靜態方法】。
接口,定義抽象即可,所以我這里都使用泛型,可以根據自己的需求定義,如果需求要限制類型也可以直接定義成具體的類型。
接口定義:
package com.stt.function.myfunction;
/**
* 自定義函數式接口:
* 定義:
* 1、接口中只有一個抽象方法
* 2、可以使用@FunctionInterface注釋修飾,也可以不使用
* 如果使用該注解報錯,說明該接口不是一個函數式接口
*/
@FunctionalInterface
public interface SttFunction<T,R,V> {
/**
* 接收兩個參數,并返回一個參數
* 注意:接口嘛,定義個大概就行了,具體什么參數,怎么返回就不需要說明了,具體實現的時候再說唄
*/
V calc(T t,R r);
}
接口使用:
package com.stt.function.myfunction;
public class SttFunctionMain {
public static void main(String[] args) {
// 1、原始方式,匿名內部類實現
SttFunction<Integer, Integer, Integer> sttFunction1 = new SttFunction<Integer, Integer, Integer>() {
@Override
public Integer calc(Integer num1, Integer num2) {
return num1 * num2;
}
};
Integer result = sttFunction1.calc(2, 2);
System.out.println(result);
// 2、Lambda表達式調用
SttFunction<Integer,Integer,Integer> sttFunction = (num1,num2) -> num1 + num2;;
Integer lambdaResult = sttFunction.calc(1023, 1);
System.out.println(lambdaResult);
}
}
Lambda表達式就是香。
包含默認實現的函數式接口:
包含默認方法和靜態方法并不影響它是一個函數式接口。
package com.stt.function.myfunction;
/**
* 自定義函數式接口:
* 定義:
* 1、接口中只有一個抽象方法
* 2、可以使用@FunctionInterface注釋修飾,也可以不使用
* 如果使用該注解報錯,說明該接口不是一個函數式接口
*/
@FunctionalInterface
public interface SttFunction<T,R,V> {
/**
* 接收兩個參數,并返回一個參數
* 注意:接口嘛,定義個大概就行了,具體什么參數,怎么返回就不需要說明了,具體實現的時候再說唄
*/
V calc(T t,R r);
default void defaultMethod(){
System.out.println("也不知道實現點什么,反正JDK8之后可以有默認實現!");
}
static void staticMethod(){
System.out.println("同樣不知道寫點什么,反正JDK8之后可以有靜態方法!");
}
}
有多個抽象方法:
有兩個以上抽象方法就不再是一個函數式接口,所以@FunctionalInterface注解報錯,該注解可以用來檢驗接口是否為一個函數式接口。

@FunctionalInterface注解
Java 8中專門為函數式接口引入了一個新的注解:@FunctionalInterface 。該注解放在接口上,表示此接口是一個函數式接口。并且提示編譯器去檢查接口是否僅包含一個抽象方法,即,是否符合函數式編程的定義。
小貼士:如果自定義一個符合規范的函數式接口,也可以不加@FunctionalInterface注解,此注解只是起到一個提示編譯器進行規范檢查的作用
總結
- 技術升級都是系統性的,僅升級修改某一部分通常是修復BUG。
- 函數式接口是Lambda的前提,JDK8之前通過匿名內部類實現,Lambda讓編碼變的簡潔。
- 函數式接口中有且僅有一個抽象方法。
- 函數式接口可以使用@FunctionalInterface檢驗,也可以不使用該注解。
- JDK內置了許多函數式接口,可以按需使用,我們也可以自定義函數式接口。
- 在閱讀部分框架源碼時一定要認識Lambda表達式和函數式接口哦?。
文章出自:??添甄??,如有轉載本文請聯系【添甄】今日頭條號。