在上一篇文章中我們介紹了JDK1.8的新特性有以下幾項。
4.默認方法
5.Stream
6.Optional類
7.Nashorm javascript引擎
8.新的日期時間API
9.Base64
之前學習了前面兩項Lambda表達式,方法引用,這一篇學習函數式接口。
所謂的函數式接口它只能定義一個抽象方法,其他方法可以用default或者static關鍵對方法進行限定。
下面先來通過實例來驗證一下。
自定義一個函數式接口,然后定義一個叫testA的抽象方法,再定義一個叫testB的抽象方法。
此時@FunctionalInterface注解會提示如下錯誤。(在接口中發現有復數個沒有被覆蓋的抽象方法)
那我接下來把testB改為非抽象方法試試。
發現改完之后又多了一處錯誤。我們繼續看看新出的錯誤。
接下來我們把方法加上default關鍵字
這時候就沒有錯誤信息啦。說明函數式接口中可以除了含有抽象方法外可以有默認的非靜態方法。
下面我把default改成static試一試看看可不可以有靜態方法。
此時發現也沒有問題。那就說明函數式接口中還可以有靜態方法。
因為Object類是所有類的父類,所有接口中能被重寫的方法都可以在接口中定義。比如
toString,equals方法。
了解完函數式接口,接下來說一下API內置的四大函數式接口。
主要就以下
1. Consumer -- 消費性接口
2. Supplier -- 供給型接口
3. Function -- 函數型接口
4. Predicate -- 斷言型接口
下面我們依次來看一下API源代碼,以及通過寫實例來理解一下每種類型接口使用方法以及特點。
1. Consumer(有傳入,沒有傳出的時候使用)
官方源代碼如下,為了起來方便簡潔把開頭的注釋去掉了,只保留了方法的注釋。
1 package java.util.function; 2 3 import java.util.Objects; 4 5 @FunctionalInterface 6 public interface Consumer<T> { 7 8 /** 9 * Performs this operation on the given argument. 10 * 11 * @param t the input argument 12 */ 13 void accept(T t); 14 15 /** 16 * Returns a composed {@code Consumer} that performs, in sequence, this 17 * operation followed by the {@code after} operation. If performing either 18 * operation throws an exception, it is relayed to the caller of the 19 * composed operation. If performing this operation throws an exception, 20 * the {@code after} operation will not be performed. 21 * 22 * @param after the operation to perform after this operation 23 * @return a composed {@code Consumer} that performs in sequence this 24 * operation followed by the {@code after} operation 25 * @throws NullPointerException if {@code after} is null 26 */ 27 default Consumer<T> andThen(Consumer<? super T> after) { 28 Objects.requireNonNull(after); 29 return (T t) -> { accept(t); after.accept(t); }; 30 } 31 }
直接看accept抽象方法,接收一個泛型T類型的t,返回值類型是void,我們把它叫做消費型接口。特點就是有去無回。
下面我們直接看實例
1 public class InfixFunctionTest { 2 //1.消費型接口 3 @Test 4 public void test1(){ 5 //把傳入的字符串打印 6 Consumer<String> con = x -> System.out.println(x);//定義函數式的實現 7 con.accept("Hello Consumer!");//接收參數 8 } 9 10 }
執行結果
com.dream.test.JDK8speciality.InfixFunctionTest,test1 Hello Consumer! Process finished with exit code 0
我們只需要在Lambda表達式的參數列表中傳入一個x,然后實現是:得到一個無返回類型的打印語句。
2. Supplier(沒有傳入,有傳出的時候使用)
官方源碼
1 package java.util.function; 2 3 /** 4 * Represents a supplier of results. 5 * 6 * <p>There is no requirement that a new or distinct result be returned each 7 * time the supplier is invoked. 8 * 9 * <p>This is a <a href="package-summary.html">functional interface</a> 10 * whose functional method is {@link #get()}. 11 * 12 * @param <T> the type of results supplied by this supplier 13 * 14 * @since 1.8 15 */ 16 @FunctionalInterface 17 public interface Supplier<T> { 18 19 /** 20 * Gets a result. 21 * 22 * @return a result 23 */ 24 T get(); 25 }
get方法沒有傳入參數,返回一個T,稱為供給型接口。特點就是只求回報不求付出。
實例
1 public class InfixFunctionTest { 2 3 //2.供給型接口 4 @Test 5 public void test2(){ 6 Supplier<String> su = () -> new String(); 7 String str = su.get(); 8 System.out.println("供給型接口的值:" + str); 9 } 10 11 }
執行結果
com.dream.test.JDK8speciality.InfixFunctionTest,test2
供給型接口的值:
Process finished with exit code 0
我們在Lambda表達式傳入參數用()代表沒有參數傳入,然后返回一個String類型的對象。
3.Function(有傳入參數,傳出的時候使用)
官方源碼
1 package java.util.function; 2 3 import java.util.Objects; 4 5 /** 6 * Represents a function that accepts one argument and produces a result. 7 * 8 * <p>This is a <a href="package-summary.html">functional interface</a> 9 * whose functional method is {@link #apply(Object)}. 10 * 11 * @param <T> the type of the input to the function 12 * @param <R> the type of the result of the function 13 * 14 * @since 1.8 15 */ 16 @FunctionalInterface 17 public interface Function<T, R> { 18 19 /** 20 * Applies this function to the given argument. 21 * 22 * @param t the function argument 23 * @return the function result 24 */ 25 R apply(T t); 26 27 /** 28 * Returns a composed function that first applies the {@code before} 29 * function to its input, and then applies this function to the result. 30 * If evaluation of either function throws an exception, it is relayed to 31 * the caller of the composed function. 32 * 33 * @param <V> the type of input to the {@code before} function, and to the 34 * composed function 35 * @param before the function to apply before this function is applied 36 * @return a composed function that first applies the {@code before} 37 * function and then applies this function 38 * @throws NullPointerException if before is null 39 * 40 * @see #andThen(Function) 41 */ 42 default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { 43 Objects.requireNonNull(before); 44 return (V v) -> apply(before.apply(v)); 45 } 46 47 /** 48 * Returns a composed function that first applies this function to 49 * its input, and then applies the {@code after} function to the result. 50 * If evaluation of either function throws an exception, it is relayed to 51 * the caller of the composed function. 52 * 53 * @param <V> the type of output of the {@code after} function, and of the 54 * composed function 55 * @param after the function to apply after this function is applied 56 * @return a composed function that first applies this function and then 57 * applies the {@code after} function 58 * @throws NullPointerException if after is null 59 * 60 * @see #compose(Function) 61 */ 62 default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { 63 Objects.requireNonNull(after); 64 return (T t) -> after.apply(apply(t)); 65 } 66 67 /** 68 * Returns a function that always returns its input argument. 69 * 70 * @param <T> the type of the input and output objects to the function 71 * @return a function that always returns its input argument 72 */ 73 static <T> Function<T, T> identity() { 74 return t -> t; 75 } 76 }
Function接口提供了一個抽象方法,兩個默認方法和一個靜態方法。這里我只對最常用的抽象方法做出解釋。
apply方法接收一個T類型的t,返回一個R類型的值。我們稱之為函數型接口。特點就是你來我往。(俗話說的好,來而不往非禮也!哈哈哈!)
實例
1 public class InfixFunctionTest { 2 3 //3.函數型接口 4 @Test 5 public void test3(){ 6 Function<String,Camera> fun = (x) -> new Camera(x);//Function抽象函數的實現。 7 Camera camera = fun.apply("Sony-A7R3");//接收傳入的參數 8 System.out.println("cameraName: " + camera.getCameraName() + " price:" + camera.getPrice()); 9 } 10 11 }
Camera類
1 //相機類 2 class Camera{ 3 //相機名字 4 private String cameraName; 5 //相機價格 6 private Integer price; 7 8 //無參數構造器 9 public Camera(){ 10 11 } 12 13 //有參數構造器 14 public Camera(String cameraName){ 15 this.cameraName = cameraName; 16 } 17 18 //有參數構造器 19 public Camera(String cameraName,Integer price){ 20 this.cameraName = cameraName; 21 this.price = price; 22 } 23 24 public String getCameraName() { 25 return cameraName; 26 } 27 28 public void setCameraName(String cameraName) { 29 this.cameraName = cameraName; 30 } 31 32 public Integer getPrice() { 33 return price; 34 } 35 36 public void setPrice(Integer price) { 37 this.price = price; 38 } 39 }
執行結果
com.dream.test.JDK8speciality.InfixFunctionTest,test3 cameraName: Sony-A7R3 price:null Process finished with exit code 0
Lam表達式中傳入一個參數x,然后x作為構造器的參數,返回一個Camera對象。
4.Predicate(傳入一個參數,返回一個布爾類型的結果)
官網源碼
1 package java.util.function; 2 3 import java.util.Objects; 4 5 /** 6 * Represents a predicate (boolean-valued function) of one argument. 7 * 8 * <p>This is a <a href="package-summary.html">functional interface</a> 9 * whose functional method is {@link #test(Object)}. 10 * 11 * @param <T> the type of the input to the predicate 12 * 13 * @since 1.8 14 */ 15 @FunctionalInterface 16 public interface Predicate<T> { 17 18 /** 19 * Evaluates this predicate on the given argument. 20 * 21 * @param t the input argument 22 * @return {@code true} if the input argument matches the predicate, 23 * otherwise {@code false} 24 */ 25 boolean test(T t); 26 27 /** 28 * Returns a composed predicate that represents a short-circuiting logical 29 * AND of this predicate and another. When evaluating the composed 30 * predicate, if this predicate is {@code false}, then the {@code other} 31 * predicate is not evaluated. 32 * 33 * <p>Any exceptions thrown during evaluation of either predicate are relayed 34 * to the caller; if evaluation of this predicate throws an exception, the 35 * {@code other} predicate will not be evaluated. 36 * 37 * @param other a predicate that will be logically-ANDed with this 38 * predicate 39 * @return a composed predicate that represents the short-circuiting logical 40 * AND of this predicate and the {@code other} predicate 41 * @throws NullPointerException if other is null 42 */ 43 default Predicate<T> and(Predicate<? super T> other) { 44 Objects.requireNonNull(other); 45 return (t) -> test(t) && other.test(t); 46 } 47 48 /** 49 * Returns a predicate that represents the logical negation of this 50 * predicate. 51 * 52 * @return a predicate that represents the logical negation of this 53 * predicate 54 */ 55 default Predicate<T> negate() { 56 return (t) -> !test(t); 57 } 58 59 /** 60 * Returns a composed predicate that represents a short-circuiting logical 61 * OR of this predicate and another. When evaluating the composed 62 * predicate, if this predicate is {@code true}, then the {@code other} 63 * predicate is not evaluated. 64 * 65 * <p>Any exceptions thrown during evaluation of either predicate are relayed 66 * to the caller; if evaluation of this predicate throws an exception, the 67 * {@code other} predicate will not be evaluated. 68 * 69 * @param other a predicate that will be logically-ORed with this 70 * predicate 71 * @return a composed predicate that represents the short-circuiting logical 72 * OR of this predicate and the {@code other} predicate 73 * @throws NullPointerException if other is null 74 */ 75 default Predicate<T> or(Predicate<? super T> other) { 76 Objects.requireNonNull(other); 77 return (t) -> test(t) || other.test(t); 78 } 79 80 /** 81 * Returns a predicate that tests if two arguments are equal according 82 * to {@link Objects#equals(Object, Object)}. 83 * 84 * @param <T> the type of arguments to the predicate 85 * @param targetRef the object reference with which to compare for equality, 86 * which may be {@code null} 87 * @return a predicate that tests if two arguments are equal according 88 * to {@link Objects#equals(Object, Object)} 89 */ 90 static <T> Predicate<T> isEqual(Object targetRef) { 91 return (null == targetRef) 92 ? Objects::isNull 93 : object -> targetRef.equals(object); 94 } 95 }
Predicate接口提供了一個抽象方法,三個默認方法和一個靜態方法。這里是對抽象方法進行舉例說明。
由於返回一個布爾類型的值,我們稱之為斷言型。特點就是發出請求,等待指示(做還是不做,哈哈)。
1 public class InfixFunctionTest { 2 3 //4.斷言式接口 4 @Test 5 public void test4(){ 6 Predicate<String> pre = x -> x == null; 7 Boolean b =pre.test(null); 8 System.out.println("斷言式測試結果:" + b); 9 } 10 11 }
執行結果
com.dream.test.JDK8speciality.InfixFunctionTest,test4 斷言式測試結果:true Process finished with exit code 0
Lambda表達式傳入一個參數x,判斷x是不是null。
以上實例中都省略了頭部的打包和導入包信息,包信息如下。
1 package com.dream.test.JDK8speciality; 2 3 import org.junit.Test; 4 5 import java.util.function.Consumer; 6 import java.util.function.Function; 7 import java.util.function.Predicate; 8 import java.util.function.Supplier;
java.util.function包下除了上面這幾個基本函數式接口外,還有好多。其他的都是這幾個基本的衍生。這幾個搞懂了,其他的就都不難理解了。有興趣的可以自己進行研究。
以上就是對函數式的學習。如果各位大神發現有不對或者不好的地方,歡迎指正。非常感謝!
上一篇文章
JDK1.8新特性之(二)--方法引用