<small id="7ktuj"></small>
      <bdo id="7ktuj"></bdo>
        <mark id="7ktuj"></mark>

        <source id="7ktuj"></source>
        <small id="7ktuj"></small>

        ITPub博客

        首頁 > 應用開發 > Java > Java之Lambda表達式使用

        Java之Lambda表達式使用

        原創 Java 作者:蝴蝶飛啊飛 時間:2019-10-12 11:05:50 0 刪除 編輯

        簡介

        Lambda 表達式(也稱閉包),是 Java8 中最受期待和歡迎的新特性之一。在 Java 語法層面 Lambda 表達式允許函數作為一個方法的參數(函數作為參數傳遞到方法中),或者把外匯返傭http://www.fx61.com/代碼看成數據。 Lambda 表達式可以簡化函數式接口的使用。函數式接口就是一個只具有一個抽象方法的普通接口,像這樣的接口就可以使用 Lambda 表達式來簡化代碼的編寫。

        使用Lambda 表達式的前提

        對應接口有且只有一個抽象方法!!!

        基礎語法

        Lambda 表達式的基礎語法: Java8 中引入了一個新的操作符 “->” 該操作符稱為箭頭操作符或 Lambda 操作符

        箭頭操作符將 Lambda 表達式拆分成兩部分:

        左側:Lambda 表達式的參數列表

        右側:Lambda 表達式中所需執行的功能, 即 Lambda

        (args1, args2...) -> {};

        Lambda 表達式的重要特征

        可選類型聲明:不需要聲明參數類型,編譯器可以統一識別參數值。

        可選的參數圓括號:一個參數無需定義圓括號,但多個參數需要定義圓括號。

        可選的大括號:如果主體包含了一個語句,就不需要使用大括號。

        可選的返回關鍵字:如果主體只有一個表達式返回值則編譯器會自動返回值,大括號需要指定明表達式返回了一個數值。

        使用Lambda 表達式的優缺點

        優點

        使用Lambda 表達式可以簡化接口匿名內部類的使用,可以減少類文件的生成,可能是未來編程的一種趨勢。

        缺點

        使用Lambda 表達式會減弱代碼的可讀性,而且 Lambda 表達式的使用局限性比較強,只能適用于接口只有一個抽象方法時使用,不宜調試。

        函數式接口

        只有函數式接口,才可以轉換為lambda 表達式

        有且只有一個抽象方法的接口被成為函數式接口!

        函數式接口可以顯式的被@FunctionalInterface 所表示,當被標識的接口不滿足規定時,編譯器會提示報錯

        案例1 無參無返回

        public class Demo01 {

            public static void main(String[] args) {

                // 1. 傳統方式 需要 new 接口的實現類來完成對接口的調用

                ICar car1 = new IcarImpl();

                car1.drive();

                // 2. 匿名內部類使用

                ICar car2 = new ICar() {

                    @Override

                    public void drive() {

                        System.out.println("Drive BMW");

                    }

                };

                car2.drive();

                // 3. 無參無返回 Lambda 表達式

                ICar car3 = () -> {System.out.println("Drive Audi");};

                car3.drive();

                // 4. 無參無返回且只有一行實現時可以去掉 {} Lambda 更簡潔

                ICar car4 = () -> System.out.println("Drive Ferrari");

                car4.drive();

                // 去查看編譯后的 class 文件 大家可以發現 使用傳統方式或匿名內部類都會生成額外的 class 文件,而 Lambda 不會

            }

        }

        interface ICar {

            void drive();

        }

        class IcarImpl implements ICar {

            @Override

            public void drive() {

                System.out.println("Drive Benz");

            }

        }

        案例2 有參有返回值

        public class Demo02 {

            public static void main(String[] args) {

                // 1. 有參無返回

                IEat eat1 = (String thing) -> System.out.println("eat " + thing);

                eat1.eat("apple");

                // 參數數據類型可以省略

                IEat eat2 = (thing) -> System.out.println("eat " + thing);

                eat2.eat("banana");

                // 2. 多個參數

                ISpeak speak1 = (who, content) -> System.out.println(who + " talk " + content);

                speak1.talk("John", "hello word");

                // 3. 返回值

                IRun run1 = () -> {

                    return 10;

                };

                run1.run();

                // 4. 返回值簡寫

                IRun run2 = () -> 10;

                run2.run();

            }

        }

        interface IEat {

            void eat(String thing);

        }

        interface ISpeak {

            void talk(String who, String content);

        }

        interface IRun {

            int run();

        }

        案例3 final 類型參數

        public class Demo03 {

            public static void main(String[] args) {

                // 全寫

                IAddition addition1 = (final int a, final int b) -> a + b;

                System.out.println(addition1.add(1, 2));

                // 簡寫

                IAddition addition2 = (a, b) -> a+b;

                System.out.println(addition2.add(2, 3));

            }

        }

        interface IAddition {

            int add(final int a, final int b);

        }

        Java8 內置的函數式接口

        Java8 提供了一個 java.util.function 包,包含了很多函數式接口,我們來介紹最為基本的 4 個(為了節省篇幅,去掉了源碼中的注釋)

        Function 接口

        @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;

            }

        }

        Function 接口的唯一抽象方法是 apply ,作用是接收一個指定類型的參數,返回一個指定類型的結果

        public class FunctionTest1 {

            public static void main(String[] args) {

                FunctionTest1 ft = new FunctionTest1();

                // 使用 lambda 表達式實現 apply 方法,返回入參 +10 。形式上如同傳遞了一個方法作為參數

                int res = ft.compute(1, v -> v + 10);

                System.out.println(res);//11

            }

            public int compute(int a, Function<Integer, Integer> function) {

                // 使用者在使用本方法時,需要去編寫自己的 apply

                // 傳遞的 funtion 是一個行為方法,而不是一個值

                return function.apply(a);

            }

        }

        默認方法compose 作用是傳入參數后,首先執行 compose 方法內的 Function apply 方法,然后將其返回值作為本 Function 方法的入參,調用 apply 后得到最后返回值

        public class FunctionTest2 {

            public static void main(String[] args) {

                FunctionTest2 ft = new FunctionTest2();

                // 調用 compose

                // +8 ,然后將得到的值 *3

                System.out.println(ft.compute(2, v -> v * 3, v -> v + 8));//30

            }

            public int compute(int a, Function<Integer, Integer> function1, Function<Integer, Integer> function2) {

                // function2 先接收入參 a ,調用 apply 后,將返回值作為新的入參,傳入 function1 ,調用 apply 返回最后結果

                return function1.compose(function2).apply(a);

            }

        }

        默認方法andThen compose 正好相反,先執行本 Function apply ,然后將結果作為 andThen 方法參數內的 Function 的入參,調用 apply 后返回最后結果

        public class FunctionTest3 {

            public static void main(String[] args) {

                FunctionTest3 ft = new FunctionTest3();

                // 調用 andThen

                // *3 ,然后將得到的值 +8

                System.out.println(ft.compute(2, v -> v * 3, v -> v + 8));//14

            }

            public int compute(int a, Function<Integer, Integer> function1, Function<Integer, Integer> function2) {

                // function2 先接收入參 a ,調用 apply 后,將返回值作為新的入參,傳入 function1 ,調用 apply 返回最后結果

                return function1.andThen(function2).apply(a);

            }

        }

        靜態方法identity 的作用是傳入啥返回啥,這里就不寫例子了

        Consumer 接口

        package java.util.function;

        import java.util.Objects;

        @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); };

            }

        }

        Consumer 接口中 accept 方法的作用是接收指定參數類型,無返回值,重點在于內部消費

        Consumer<String> consumer = s -> System.out.println("hello " + s);

        consumer.accept("mike");// hello mike

        默認方法andThen 作用是連續消費,從本 Consumer 開始,從外到內,針對同一入參。

        Consumer<String> consumer = s -> System.out.println("hello " + s);

        Consumer<String> consumer2 = s -> System.out.println("nice to meet you " + s);

        consumer.andThen(consumer2).accept("mike");

        //hello mike

        //nice to meet you mike

        Predicate 接口

        package java.util.function;

        import java.util.Objects;

        @FunctionalInterface

        public interface Predicate<T> {

            boolean test(T t);

            default Predicate<T> and(Predicate<? super T> other) {

                Objects.requireNonNull(other);

                return (t) -> test(t) && other.test(t);

            }

            default Predicate<T> negate() {

                return (t) -> !test(t);

            }

            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);

            }

        }

        Predicate 中的 test 方法,傳入指定類型參數,返回布爾類型的結果,用于判斷,斷言

        // 判斷一個數是否是偶數

        Predicate<Integer> predicate = b -> n % 2 == 0;

        System.out.println(predicate.test(3));//false

        默認方法and 顧名思義,將本 Predicate and 參數中的 Predicate 對同一入參進行 test 的結果進行【與】操作。

        negate 方法對 test 的結果進行【非】操作

        or 方法對兩個 Predicate test 結果進行【或】操作

        靜態方法isEqual 將其入參與 test 方法的入參進行 equals 比較

        System.out.println(Predicate.isEqual(1).test(1));//true

        Supplier 接口

        package java.util.function;

        @FunctionalInterface

        public interface Supplier<T> {

            T get();

        }

        Supplier 意為供應,只有一個方法 get ,不接收任何參數,只返回指定類型結果

        Supplier<String> sup = () -> "hello world";

        System.out.println(sup.get());

        常用方法:

        1 Stream filter(Predicate<? super T> predicate); 過濾(方法參數有參有返回值,返回值為 boolean 類型) boolean test(T t)

        2 Stream map(Function<? super T, ? extends R> mapper); 將當前流中的 T 類型數據轉換為另一種 R 類型的流。(有參有返回值) R apply(T t)

        3 void forEach(Consumer<? super T> action); (有參無返回值) void accept(T t)

        4 函數原型為Stream distinct() ,作用是返回一個去除重復元素之后的 Stream

        5 sorted()

        排序函數有兩個,一個是用自然順序排序,一個是使用自定義比較器排序,函數原型分別為Stream  sorted() Stream  sorted(Comparator<? super T> comparator)

        Comparator 接口方法: int compare(T o1, T o2)

        Stream stream= Stream.of(“I”, “love”, “you”, “too”);stream.sorted((str1, str2) -> str1.length()-str2.length()) .forEach(str -> System.out.println(str));6 Collect

        // Stream 轉換成容器或 Map

        Stream stream = Stream.of(“I”, “love”, “you”, “too”);

        List list = stream.collect(Collectors.toList());

        Set set = stream.collect(Collectors.toSet());

        Map<String, Integer> map = stream.collect(Collectors.toMap(Function.identity(), String::length)); 上述代碼能夠滿足大部分需求,但由于返回結果是接口類型,我們并不知道類庫實際選擇的容器類型是什么,有時候我們可能會想要人為指定容器的實際類型,這個需求可通過 Collectors.toCollection(Supplier collectionFactory) 方法完成。 // 使用 toCollection() 指定規約容器的類型

        ArrayList arrayList = stream.collect(Collectors.toCollection(ArrayList::new));

        HashSet hashSet = stream.collect(Collectors.toCollection(HashSet::new)); 使用 collect() 生成 Map

        1. 使用 Collectors.toMap() 生成的收集器,用戶需要指定如何生成 Map key value

        2. 使用 Collectors.partitioningBy() 生成的收集器,對元素進行二分區操作時用到。

        3. 使用 Collectors.groupingBy() 生成的收集器,對元素做 group 操作時用到。


        來自 “ ITPUB博客 ” ,鏈接:http://www.ep4tq.com/69946279/viewspace-2659652/,如需轉載,請注明出處,否則將追究法律責任。

        請登錄后發表評論 登錄
        全部評論
        管他誰是誰非,做自己的主宰,我是這條街最亮的崽!

        注冊時間:2019-08-22

        • 博文量
          49
        • 訪問量
          21835
        妹子图每日分享