lambda之常用函數式接口


函數式接口和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提供了大量的函數式接口。用以應對不同的應用場景。

目錄

  飛回


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM