概述
java8中新增了 @FunctionalInterface 注解表示函數(shù)式接口掸掸,用注解@FunctionalInterface標識的接口都是函數(shù)式接口杉畜,函數(shù)式接口只能有一個抽象方法纪蜒,但除了抽象方法,java8還允許接口中定義默認方法(聲明為default的方法)和靜態(tài)方法此叠。如果一個接口只有一個抽象方法纯续,即便我們并沒有用 @FunctionalInterface 注解標注随珠,編譯器依然會將該接口看做是函數(shù)式接口。如果我們自己定義的接口只有一個抽象方法猬错,加不加 @FunctionalInterface 注解都可以窗看,如果加了的話,編譯器就會做函數(shù)式接口檢查倦炒,不滿足函數(shù)式接口的要求就會報出相應(yīng)的異常显沈,編譯通不過的。
但是有一點特殊情況逢唤,就是函數(shù)式接口中如果重寫了Object類中聲明為public的方法拉讯,那么編譯器并不認為這是一個抽象方法。因為java中所有的類都是Object的子類鳖藕,我們函數(shù)式接口中重寫了的Object類中聲明為public的方法可以被實現(xiàn)函數(shù)式接口的類直接從Object類繼承魔慷,所以編譯器并不把重寫了Object類中聲明為public的方法當做抽象方法。
@FunctionalInterface
public interface MyInterface {
void hello();
String toString(); //重寫了Object類中的toString方法
}
四大函數(shù)式接口
java8中內(nèi)置了四大函數(shù)式接口著恩,通過這四大函數(shù)式接口我們可以完成大多數(shù)的需求院尔,如果這四大函數(shù)接口不能滿足需求,java中還擴展了其他的函數(shù)式接口喉誊,并且也可以自己定義函數(shù)式接口邀摆。
1.Consumer接口
Consumer是一個消費者接口,accpet(T t)方法給定一個參數(shù)伍茄,把它消費了栋盹,不返回任何結(jié)果。Consumer函數(shù)式接口的源碼如下:
@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:
Consumer<Integer> consumer = (i) -> System.out.println(i);
consumer.accept(666);
示例2:
public class Test01 {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9);
list.forEach(i -> System.out.println(i));
}
}
forEach()方法源碼跟蹤:
forEach()方法位于Iterable<T>接口中幻林,參數(shù)是一個Consumer接口類型贞盯。
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
List<>接口繼承了Collection<E>接口,而Collection<E>接口又繼承了Iterable<E>接口沪饺,所以List可以使用forEach()方法遍歷元素躏敢。
public interface List<E> extends Collection<E> {...}
public interface Collection<E> extends Iterable<E> {...}
2.Function接口
Function接口中的抽象方法為 apply(T t),該方法有一個參數(shù),并返回結(jié)果整葡。除了apply這個抽象方法件余,還有其他的默認方法和靜態(tài)方法,源碼如下:
@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;
}
}
示例1:
public class FunctionTest {
public static void main(String[] args) {
System.out.println(compute(2,i -> i * 3));
System.out.println(compute(2,i -> i*i));
System.out.println(compute(2,i -> i + 3));
}
private static int compute(int a,Function<Integer,Integer> function){
int result = function.apply(a);
return result;
}
}
compose和andThen方法分析:
compose方法是將兩個Function組合在一起遭居,先調(diào)用參數(shù)里給定的Function的apply方法:before.apply(v)啼器,再將參數(shù)里的Function的結(jié)果給當前的Function(也就是作為返回值的Function)的apply方法作參數(shù):apply(before.apply(v)),將結(jié)果返回俱萍。
andThen方法也是將兩個Function組合在一起端壳,但是andThen方法正好和compose方法相反,是先調(diào)用當前的Function的apply方法:apply(t)枪蘑,再將當前的Function的結(jié)果給參數(shù)里的Function的apply方法作參數(shù):after.apply(apply(t))损谦,將結(jié)果返回岖免。
示例2
public class FunctionTest {
public static void main(String[] args) {
System.out.println(compute1(2,i -> i * 3,i -> i * i));
System.out.println(compute2(2,i -> i * 3,i -> i * i));
}
private static int compute1(int a,Function<Integer,Integer> function1,Function<Integer,Integer> function2){
return function1.compose(function2).apply(a);
}
private static int compute2(int a,Function<Integer,Integer> function1,Function<Integer,Integer> function2){
return function1.andThen(function2).apply(a);
}
}
補充:以上的Function函數(shù)式接口只能傳入一個參數(shù),并返回結(jié)果照捡,如果要傳入兩個參數(shù)并返回執(zhí)行結(jié)果颅湘,F(xiàn)unction函數(shù)式接口是做不到的,那么這時可以使用通過Function接口擴展的BiFunction函數(shù)式接口栗精,該接口有三個泛型T闯参,U,R悲立,前兩個作為方法的輸入?yún)?shù)類型鹿寨,最后一個作為返回結(jié)果類型。
該接口源碼如下:
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t, U u) -> after.apply(apply(t, u));
}
}
示例3:
public class FunctionTest {
public static void main(String[] args) {
System.out.println(compute3(2,3,(i,j) -> i + j));
System.out.println(compute3(2,3,(i,j) -> i * j));
}
private static int compute3(int a, int b, BiFunction<Integer,Integer,Integer> biFunction){
return biFunction.apply(a,b);
}
}
通過BiFunction接口的源碼级历,我們可以知道释移,該接口有andThen這個默認方法叭披,沒有compose方法寥殖,那么為什么BiFunction接口沒有提供compose方法呢?其實這點也是很好理解的涩蜘,假設(shè)BiFunction接口可以提供compose方法,并假設(shè)定義如下:
default <V> BiFunction<V, T, R> compose(BiFunction<? super V, ? extends T,? extends U> before) {
Objects.requireNonNull(before);
return (V v , T t) ->apply(before.apply(v, t));
}
依照上面我們對Funciton接口的compose方法的分析嚼贡,先調(diào)用參數(shù)里給定的BiFunction的apply方法:before.apply(v,u),再將參數(shù)里的BiFunction的結(jié)果給當前的BiFunction的apply方法作參數(shù)同诫,將結(jié)果返回粤策。
因為BiFunction的apply方法有兩個參數(shù),而Function和BiFunction接口的apply方法都只有一個返回值误窖,所以不論參數(shù)里給定的是Funciton類型叮盘,還是BiFunction類型,都是不滿足要求的霹俺。
下面分析BiFunction的andThen方法:
BiFunction的andThen方法的返回值類型是BiFunction,參數(shù)類型是Function,這和上面的Function的andThen方法是不一樣的柔吼,那么為什么BiFunction的andThen方法的的參是Function類型,而不是BiFunction類型呢丙唧?其實這點也不能理解愈魏。
andThen方法是先調(diào)用當前的BiFunction的apply方法:apply(t, u),再將當前的BiFunction的結(jié)果給參數(shù)里的Function的apply方法作參數(shù),又因為 R apply(T t, U u)想际,BiFunction的apply方法的返回值只有一個培漏,所以參數(shù)里只能是Function類型,如果參數(shù)里也是BiFunction類型胡本,那么需要BiFunction的apply方法有兩個參數(shù)牌柄。
示例4:
public class FunctionTest {
public static void main(String[] args) {
System.out.println(compute4(2,3,(i,j) -> i + j,i -> i * i));
}
private static int compute4(int a,int b,BiFunction<Integer,Integer,Integer> biFunction,
Function<Integer,Integer> function){
return biFunction.andThen(function).apply(a,b);
}
}
3.Predicate接口
Predicate是一個斷言接口,給定一個參數(shù)侧甫,返回一個boolean值珊佣,即給定一個條件返回true或者false傻昙。其源碼如下:
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
//返回兩個Predicate的邏輯與的結(jié)果
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
//返回predicate取反后的結(jié)果
default Predicate<T> negate() {
return (t) -> !test(t);
}
//返回兩個predicate邏輯或后的結(jié)果
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);
}
}
示例1:
public class PredicateTest {
public static void main(String[] args) {
Predicate<Integer> predicate = i -> i > 5;
System.out.println(predicate.test(4));
System.out.println(predicate.test(6));
}
}
示例2:
public class PredicateTest {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
conditionFilter(list,i -> i % 2 == 0);
}
private static void conditionFilter(List<Integer> list,Predicate<Integer> predicate){
for (Integer integer : list) {
if(predicate.test(integer)){
System.out.println(integer);
}
}
}
}
示例3(and方法示例):
public class PredicateTest {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
//找出集合list中大于5并且是偶數(shù)的數(shù)
conditionFilter1(list,i -> i > 5, i -> i % 2 == 0);
}
private static void conditionFilter1(List<Integer> list,Predicate<Integer> predicate1,Predicate<Integer> predicate2){
for (Integer integer : list) {
if(predicate1.and(predicate2).test(integer)){
System.out.println(integer);
}
}
}
}
示例4(or方法示例):
public class PredicateTest {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
//找出集合list中大于5或者是偶數(shù)的數(shù)
conditionFilter2(list,i -> i > 5,i -> i % 2 == 0 );
}
private static void conditionFilter2(List<Integer> list,Predicate<Integer> predicate1,Predicate<Integer> predicate2){
for (Integer integer : list) {
if(predicate1.or(predicate2).test(integer)){
System.out.println(integer);
}
}
}
}
示例5(negate方法示例):
public class PredicateTest {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
//找出集合list中的所有奇數(shù)
conditionFilter3(list,i -> i % 2 == 0);
}
private static void conditionFilter3(List<Integer> list,Predicate<Integer> predicate){
for (Integer integer : list) {
if(predicate.negate().test(integer)){
System.out.println(integer);
}
}
}
}
4.Supplier接口
Supplier是一個提供者接口,不接受任何參數(shù)彩扔,同時返回一個結(jié)果妆档,其源碼如下:
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
示例:
public class Teacher {
private String tName = "zhangkai";
public String gettName() {
return tName;
}
public void settName(String tName) {
this.tName = tName;
}
}
public class SupplierTest {
public static void main(String[] args) {
Supplier<Teacher> supplier = () -> new Teacher();
System.out.println(supplier.get().gettName());
}
}
總結(jié)
寫到這里總算結(jié)束了,可能篇幅有點長虫碉,看起來心情有點不爽贾惦,當我看別人的播客文章的時候,太長了也會感覺很無聊敦捧,不想繼續(xù)看下去须板,自己寫的時候也長篇大論了,寫的篇幅長的主要原因是扣的太細了兢卵,接口里的其他默認方法也分析了一下习瑰,而那些默認方法可能實際中用到的比較少,甚至幾乎就用不到秽荤,這么做也主要是想對函數(shù)式接口了解更多一點甜奄,不至于只知道函數(shù)式接口中常用的抽象方法,也是為了我個人以后哪天看看自己寫的播客復(fù)習一下窃款,人的遺忘真的是太快了课兄,尤其是計算機行業(yè),要學(xué)好多東西晨继,雖學(xué)的多烟阐,但如果沒有輸出,沒有筆記的話紊扬,真的過段時間就一點都沒了蜒茄,忘得很干凈,提起來只有一個印象餐屎,具體的用法以及細節(jié)已經(jīng)想不起來了檀葛,這時自己的播客文章或筆記看一遍,就又都拾起來了啤挎。如果讀者覺得沒必要看那么細驻谆,也許我寫的還有些復(fù)雜,有些廢話連篇庆聘,那么不常用的那些默認方法可以不看胜臊,直接跳過即可。當然我也不能面面具到伙判,由著四大函數(shù)式接口可以擴展出其他很多的函數(shù)式接口象对,比如出入兩個參數(shù)的BiConsumer,BiPredicate等等,用到的時候大家可以查下jdk的api文檔接口宴抚。