Java粗淺認識-Java 8 lambda 表達式

一、lambda表達式實例

提到lambda表達式,我們一般用的最多的就是foreach遍歷List、Set、Queue、Map等。

/**
     * 容器forEache遍歷
     */
    public static void forEach() {
        //list
        List<String> list = new ArrayList<>();
        list.forEach(s -> System.out.println(s));

        //set
        Set<String> set = new HashSet<>();
        set.forEach(s -> System.out.println(s));

        //queue
        Queue<String> queue = new PriorityQueue();
        queue.forEach(s -> System.out.println(s));

        //deque
        Deque<String> deque = new LinkedList();
        deque.forEach(s -> System.out.println(s));

        //Map
        Map<String, Object> map = new HashMap();
        map.forEach((s, o) -> System.out.println(s + ":" + o));
    }

當然,我們可以自己實現寫一個函數式接口,然後通過lambda表達式來編寫我們自己的代碼。

    public static void main(String[] args) {
        fooFun("測試打印效果",target -> System.out.println(target));
    }

    /**
     * 隨意一個方法
     * @param target 需要被答應的內容
     * @param printer 打印機
     */
    public static void fooFun(String target,FunctionInterface printer){
        printer.print(target);
    }

    /**
     * 函數式接口,@FunctionalInterface可加可不加,FunctionalInterface屬於target=Runtime
     * ,編譯時已經被jvm處理過
     */
    @FunctionalInterface
    public interface FunctionInterface<T>{
        /**
         * 接口函數
         * @param target 需要被打印的目標
         */
        void print(T target);
    }

 分析:

1.定義一個函數式接口(對應的,public interface FunctionInterface<T>);

2.在自己定義的方法中,傳入這個已經定義好的函數式接口(fooFun(String target,FunctionInterface printer););

3在使用方法時,就能用lambda表達表達式了。(fooFun("測試打印效果",target -> System.out.println(target));)

二、什麼是lambda表達式

這裏演示了lambda表達式的基本使用,接下來我們來看看什麼是lambda表達式。

lambda表達式我們可以理解爲傳遞匿名函數的一種方式,這種表達式沒有方法名,有參數列表,有方法體,有返回值,可能有異常拋出(通常不推薦拋出異常,java默認實現也沒有拋出異常,只有自己實現時,纔會根據需要拋出),能夠當作參數帶入方法中。

lambda表達式
lambda表達式

 總結起來分兩種:

(parameters) -> expression

(parameters) -> {statements;}

三、函數式接口、接口函數、靜態方法、默認方法、方法引用

上文提到了函數式接口,接口方法,接下來我們瞭解一下什麼是函數式接口,什麼是接口方法。

函數式接口就是隻定義了一個抽象方法的接口。

/**
 * @author baopz
 */
@FunctionalInterface
public interface FunctionInterface {
    @Override
    int hashCode();

    @Override
    boolean equals(Object obj);

    @Override
    String toString();

    /**
     * 唯一的抽象方法
     */
    void test();

    /**
     * 默認方法
     */
    default void foo() {}

    /**
     * 靜態方法
     */
    static void bar() {}
}

 分析:

1.代碼中,接口Override了來自Object的三個方法hashCode()、equals()、toString()

2.唯一的抽象方法test(),用來在lambda表達式中唯一實現的方法,也就是接口函數

3.有個帶有方法體的,由default修飾的默認方法foo(),可以理解爲實現了該接口的子類,會順帶繼承的方法,默認方法

4.有個帶有方法體的,由static修飾的靜態方法bar(),可以理解爲工具方法,靜態方法

方法引用,僅僅涉及到單一方法的使用時,提供的一種更加簡潔的寫法的語法糖。

四、方法引用

指向靜態方法的方法引用,Integer::parseInt
指向任意類型實例方法的引用,String::length
指向現有對象的實例方法的方法引用,methodReferences::length
構造函數數組構造函數父類調用(super-call)的一些特殊形式的方法引用

實例:

/**
 * 方法引用事例
 * @author baopz
 */
public class MethodReferences {
    public static void main(String[] args) {
        //靜態方法的方法引用
        staticFun("123", Integer::parseInt);

        //任意類型實例方法的引用
        instanceFun("foo", String::length);

        //構造函數
        MethodReferences methodReferences = createInstance(MethodReferences::new);

        //數組構造函數
        MethodReferences[] methodRes = createArrayInstance(3, MethodReferences[]::new);

        //現有對象的實例方法的方法引用
        curInstanceFun("foo", methodReferences::length);
    }

    public static void staticFun(String str, Consumer<String> consumer) {
        consumer.accept(str);
    }

    public static void instanceFun(String str, Consumer<String> consumer) {
        consumer.accept(str);
    }

    public static MethodReferences createInstance(Supplier<MethodReferences> supplier) {
        return supplier.get();
    }

    public static MethodReferences[] createArrayInstance(int length, Function<Integer, MethodReferences[]> function) {
       return function.apply(length);
    }

    public static int curInstanceFun(String str, Function<String, Integer> function) {
        return function.apply(str);
    }

    public int length(String str) {
        return str.length();
    }
}

五、java默認提供的幾類函數式接口

上文中使用了Function,Supplier,Consumer等接口,這些接口都來自,java1.8 新增的java.util.function包中,是java自帶的一些默認函數式接口

```

Predicate<T>           T->boolean
Function<T,R>         T->R
Consumer<T>         T-void
Supplier<T>            void -> T
...

數值類型函數式接口

```

* Predicate<T>,謂詞,語義是傳入任意一個類型T,經過處理(Predicate的接口方法test()),返回一個boolean類型的值。

    public static boolean isDog(Dog dog, Predicate<Dog> predicate){
        return predicate.test(dog);
    }
   
    public static void main(String[] args) {
        Dog.isDog(new Dog(),dog -> dog instanceof Dog);
    }

*  Function<T,R> ,方法,語義是傳入一個任意類型T,經過處理(Function 的接口方法apply()),返回一個R類型的值

    public static void main(String[] args) {
        //基礎lambda表達式
        Bar.length("foo",s -> s.length());
        //方法引用的寫法
        Bar.length("foo",String::length);
    }

    public static int length(String foo, Function<String,Integer> function){
        return function.apply(foo);
    }

* Consumer<T>,消費者,語義是傳入一個任意類型的T,用方法處理(Consumer的接口方法accept()),返回一個void(就是沒有返回值)。

    public static void main(String[] args) {
        Dog.run(new Dog(),dog -> System.out.println("dog running."));
    }

    public static void run(Dog dog, Consumer<Dog> consumer){
        consumer.accept(dog);
    }

* Supplier<T>,提供者,語義是不用傳遞任何參數,直接通過方法(Supplier的接口方法get())產生一個T類型,並返回。

    public static void main(String[] args) {
        //lambda基本表達式
        Dog dog = Dog.get(() -> new Dog());
        //方法引用寫法
        dog = Dog.get(Dog::new);
    }

    public static Dog get(Supplier<Dog> supplier){
        return supplier.get();
    }

數值類型函數式接口,我們都知道在java中對象類型分兩類,基礎類型和引用類型,得益於java1.5提供的類型自動裝箱,自動拆箱,用Integer、Double、Long等計算是拆箱爲int、double、long等來進行數值計算,但自動裝拆箱有效率損耗,所以lambda爲了消除這種效率損耗,提供了數值類型函數式接口,如IntFunction、DoublePredicate、LongConsumer等(還有很多)

    public static void main(String[] args) {
        add(10, 20, (left, right) -> left + right);
    }

    public static int add(int a, int b, IntBinaryOperator operator) {
        return operator.applyAsInt(a, b);
    }

這裏類型比較多,就不一一介紹,建議各位小夥伴如果要深入學習,建議把所有的類型都練習一遍。

由於類型比較多,類名不容易記住,java對這些類的命名有個小訣竅,

就是針對專門的輸入參數類型的函數式接口,

1.名稱都要加上對應的原始類型前綴(DoubleConsumer),

2.如果輸入參數有兩個的,都有Binary關鍵字(LongBinaryOperator),

3.如果沒有具體類型的兩個參數類型傳入,則是以Bi爲前綴(BiConsumer)

4.如果只知道返回類型的,都是以To爲前綴(ToIntFunction)

六、思考

java1.8的lambda內容講完了這裏給大家留個小問題,如果傳入的參數是三個或多個,該如何處理呢? 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章