函數式接口和lambda表達式關聯
函數式接口只能操作一個方法,而λ表達式也只能操作一個方法,λ表達式其實核心就是一個函數式接口的實現。
正常創建訂單和匿名創建訂單
訂單類
package liojx.kn.fnI; import java.math.BigDecimal; import java.util.Date; public class Order { /** 訂單編號 */ private String orderCode; /** 訂單創建時間 */ private Date createTime; /** 訂單總價 */ private BigDecimal totalPrice; /** 商品數量 */ private int count; /** 商品 */ private String product; public Order() { } public Order(String orderCode, Date createTime, BigDecimal totalPrice, int count, String product) { this.orderCode = orderCode; this.createTime = createTime; this.totalPrice = totalPrice; this.count = count; this.product = product; } public String getOrderCode() { return orderCode; } public void setOrderCode(String orderCode) { this.orderCode = orderCode; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public BigDecimal getTotalPrice() { return totalPrice; } public void setTotalPrice(BigDecimal totalPrice) { this.totalPrice = totalPrice; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } public String getProduct() { return product; } public void setProduct(String product) { this.product = product; } @Override public String toString() { return "Order{" + "orderCode='" + orderCode + '\'' + ", createTime=" + createTime + ", totalPrice=" + totalPrice + ", count=" + count + ", product='" + product + '\'' + '}'; } }
創建訂單的main方法
package liojx.kn; import liojx.kn.fnI.CreateFoodOrderImpl; import liojx.kn.fnI.CreateOrder; import liojx.kn.fnI.Order; import java.math.BigDecimal; import java.util.Date; /** * Hello world! * */ public class App { public static void main( String[] args ) { CreateOrder cfo1 = new CreateFoodOrderImpl(); System.out.println(cfo1.createOrder("霸王龍兩只")); CreateOrder cfo2 = new CreateOrder() { @Override public Order createOrder(String info) { return new Order("9099XY013", new Date(), new BigDecimal(892.3), 2, info); } }; System.out.println(cfo2.createOrder("三角龍兩只")); CreateOrder cfo3 = (String info) -> { return new Order("9099XY014", new Date(), new BigDecimal(32.3), 2, info); }; System.out.println(cfo3.createOrder("甲龍兩只")); } }
運行結果
Order{orderCode='9099XY012', createTime=Fri Aug 21 15:48:26 CST 2020, totalPrice=239, count=2, product='霸王龍兩只'} Order{orderCode='9099XY013', createTime=Fri Aug 21 15:48:26 CST 2020, totalPrice=892.299999999999954525264911353588104248046875, count=2, product='三角龍兩只'} Order{orderCode='9099XY014', createTime=Fri Aug 21 15:48:26 CST 2020, totalPrice=32.2999999999999971578290569595992565155029296875, count=2, product='甲龍兩只'}
第一種方式,采用CreateFoodOrderImpl正常實現函數式接口CreateOrder 來創建兩只霸王龍的漢堡包。
第二種方式,采用java匿名內部類的方式創建兩只三角龍的漢堡包。
第三種方式,采用的是λ表達式來創建兩只甲龍的漢堡吧。
其實就是一句話的事:我要一個[傳入參數]類型的漢堡包,第一種還創建了實現類,第二種匿名類卻簡潔了很多,而第三種λ利用語法糖讓代碼看起來更簡潔。
常用函數式接口
Predicate
點開Predicate類,首先看到是@since 1.8 , 接着被@FunctionalInterface標記。同時里面有5個方法,其中方法and、or、negate都是默認方法,isEqual是靜態方法,而test
才是它的核心方法。按照咱們之前的理論,一個函數式接口僅有一個抽象方法,即
boolean test(T t);
該方法接收一個參數T,返回一個布爾類型,是一個驗證功能的函數。
/** * 創建一個Predicate並實現判斷邏輯,判斷它是否是蘋果 */ Predicate<String> predicate = (String pram) -> { return "apple".equals(pram) ? true : false; }; System.out.println(predicate.test("apple")); // 返回true
and 函數的源碼
default Predicate<T> and(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) && other.test(t); }
可以看出and 函數表示傳入的other對象(T 的子類參數的Predicate對象)需要既滿足當前類的test方法,又要滿足參數對象類的test方法,返回一個Predicate對象。
代碼示例
Predicate<String> pre1 = (String param) -> { return param.length() >= 5; }; Predicate<String> pre2 = (String param) -> { return "L".equals(param.charAt(5) + ""); }; Predicate<String> pre3 = pre1.and(pre2); // and 方法 System.out.println(pre1.test("apple")); // true System.out.println(pre2.test("alibaLba")); //true System.out.println(pre3.test("appleLhb")); // true 滿足兩個條件:字符數大於等於5,第6個字符為L System.out.println(pre3.test("applehhb")); // false 滿足一個條件:字符數大於等於5,第6個字符為h
negate函數:取反
default Predicate<T> negate() { return (t) -> !test(t); }
Predicate<String> pre1 = (String param) -> { return param.length() >= 5; }; Predicate<String> pre4 = pre1.negate(); System.out.println(pre4.test("aaaa")); // true System.out.println(pre4.test("aaaaa")); // false
同理,or函數,取並集,isEqual判斷是否相等。
Consumer
它的函數式接收一個T對象,函數處理后,不返回任何結果。
void accept(T t);
譬如, 傳入一個Order對象,打印它每個字段的值。
Consumer<Order> con = (Order order) -> { System.out.println(order.toString()); }; con.accept(new Order("9099XY015", new Date(), new BigDecimal(32.3), 2, "風神翼龍")); // 輸出結果
// Order{orderCode='9099XY015', createTime=Fri Aug 21 17:05:51 CST 2020, totalPrice=32.2999999999999971578290569595992565155029296875, count=2, product='風神翼龍'}
它還提供一個默認的方法andThen, 意為先處理,緊接着又處理。
Consumer<Order> con = (Order order) -> { System.out.println(order.toString()); }; con.accept(new Order("9099XY015", new Date(), new BigDecimal(32.3), 2, "風神翼龍")); Consumer<Order> con2 = (Order o) -> { System.out.println(o.getTotalPrice().add(new BigDecimal(200))); }; con = con.andThen(con2); // andThen 的前面對象方法先執行,參數對象的方法后執行 con.accept(new Order("9099XY015", new Date(), new BigDecimal(32.3), 2, "風神翼龍"));
// 結果
// Order{orderCode='9099XY015', createTime=Fri Aug 21 17:12:48 CST 2020, totalPrice=32.2999999999999971578290569595992565155029296875, count=2, product='風神翼龍'}
// Order{orderCode='9099XY015', createTime=Fri Aug 21 17:12:48 CST 2020, totalPrice=32.2999999999999971578290569595992565155029296875, count=2, product='風神翼龍'}
// 232.2999999999999971578290569595992565155029296875
Function<T, R>
/** * Applies this function to the given argument. * * @param t the function argument * @return the function result */ R apply(T t);
它接收一個T對象,計算后返回一個R對象。
Function<String, Integer> fun = (String t) -> { String [] arr = t.split(""); return Integer.parseInt(arr[1]) + 200; }; System.out.println(fun.apply("23123")); // 203
默認方法 compose 和 andThen,區別在於前者是傳入的參數先執行,后者是傳入的參數后執行,看示例
Function<String, Integer> fun = (String t) -> { String [] arr = t.split(""); return Integer.parseInt(arr[1]) + 200; }; Function<Integer, String> fun2 = (Integer t) -> { return String.valueOf(t); }; Function fun3 = fun.compose(fun2); System.out.println(fun3.apply(8999993)); // 209 先執行fun2 再執行fun System.out.println(fun3.apply(8999993) instanceof Integer); // true Function fun4 = fun.andThen(fun2); // System.out.println(fun4.apply(8999993)); // 先執行fun 在執行fun2 // 報錯 Exception in thread "main" // java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String // 由於fun執行完了返回的是String對象,緊接着把fun返回的對象作為fun2的參數對象,fun2參數需要Integer,故報類型錯誤 System.out.println(fun4.apply("8999993")); // 209 先執行fun 在執行fun2 System.out.println(fun4.apply("8999993") instanceof Integer); // false
可以看出,他們區別即是先后順序。
identity() 是一個無參數的靜態方法,傳入什么返回什么
// identity 傳進來什么就返回什么,類似於 t-> t,實際代碼也是這樣 Stream<String> stream = Stream.of("a","bb","ccc","dddd","eeeee","ffffff"); Stream<String> stream1 = Stream.of("a","bb","ccc","dddd","eeeee","ffffff"); Map<String, Integer> collect = stream.collect(Collectors.toMap(Function.identity(), String::length)); Map<String, Integer> collect2 = stream1.collect(Collectors.toMap(t -> t, String::length)); System.out.println(collect); // {bb=2, a=1, ccc=3, ffffff=6, eeeee=5, dddd=4} System.out.println(collect2); // {bb=2, a=1, ccc=3, ffffff=6, eeeee=5, dddd=4}
Supplier<T>
supplier提供了一個get方法,返回一個泛型的T對像。
Supplier<String> sup = () -> { Stream<String> st = Stream.of("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p", "q","r","s","t","u","v","w","x","y","z"); Random r = new Random(); double v = 0; List<String> list = st.collect(Collectors.toList()); StringBuffer sb = new StringBuffer(); int len = 10, word = 3; for (int i = 0; i < word; i++){ for (int j = 0 ;j < len; j++ ){ int a = -1; while(a > 25 || a < 0) { v = r.nextDouble(); a = (int)(v*100); } sb.append(list.get(a)); } sb.append(" "); } return sb.toString(); }; System.out.println(sup.get());
UnaryOperator<T>
unaryOperator本身沒有抽象方法,它繼承自Function<T, T>,區別在於function是接收一個參數T,返回一個對象R。而unaryOperator是接受一個參數T,返回一個T對象。
UnaryOperator<Integer> unaryOperator = (Integer i) -> { return i + 23; // 這里返回int,返回其它類型就報錯 }; System.out.println(unaryOperator.apply(30)); // 53
BinaryOperator<T>
binaryOperator繼承自BiFunction<T,T,T>,BiFunction的方法是
/** * Applies this function to the given arguments. * * @param t the first function argument * @param u the second function argument * @return the function result */ R apply(T t, U u);
接收一個參數T和一個參數U,返回一個R。
例如:
BinaryOperator<Integer> binaryOperator = (Integer i1, Integer i2) -> { return i1 > i2 ? i1 : i2; }; System.out.println(binaryOperator.apply(50,70)); // 70
總結:上面都是常用的函數式接口,而java.util.function提供了大量的函數式接口。用以應對不同的應用場景。