1. 程式人生 > >Java8新特性之Lambda表示式學習一

Java8新特性之Lambda表示式學習一

       剛畢業入職新工作,在職崗位培訓時,老師在操作集合老使用Lambda表示式。這使一個之前完全沒有接觸過Lambda表示式的少年甚是苦惱,看不懂,閒餘時間決定搞一搞Lambda表示式到底是啥東西?底層原理怎麼實現的,接下來我將我的學習成果一起分享給大家......

一、Lambda表示式的簡介:

       Lambda表示式首先不是一種新的知識點,而是Java8推出來的一種新的語法。這種新的語法背後仍然是介面、方法、引數、返回值、匿名內部類等。並且這種語法方便程式設計師編寫,但是JVM在編譯時,會自動還原以前我們自己寫的程式碼。

例1:對其學生分數進行升序排序: 

public class Test {
    class User{
        private String name;
        private Integer score;

        public User(String name, Integer score) {
            this.name = name;
            this.score = score;
        }

        @Override
        public String toString() {
            return this.name + this.score;
        }
    }

    /**
     * 傳統的寫法
     */
    public void testOldUse(){
        User[] users = new User[]{new User("張三",80),new User("李四",81),new User("王五",82)};
        //對User陣列按照score排序
        Arrays.sort(users, new Comparator<User>() {
            public int compare(User o1, User o2) {
                //有可能兩個數相減,超出int範圍,所以我們借用Integer中的compare()方法
                //return o1.score - o2.score;
                return Integer.compare(o1.score, o2.score);
            }
        });
        System.out.println(Arrays.toString(users));
    }

    /**
     * 使用lambda表示式
     */
    @Test
    public void testNewUse(){
        User[] users = new User[]{new User("張三",83),new User("李四",84),new User("王五",85)};
        //對User陣列按照score排序new Comparator<User>() {
        //Lambda表示式 (切記三個一定)
        /*  第一:Arrays.sort()這個方法的第二引數一定是實現了Comparator介面;
         *  第二:實現Comparator介面,一定要重寫compare()方法;
         *  第三:方法compare()執行完畢,一定會返回一個int型別的值;
         *  這些JVM在編譯的時候,會自動的幫助我們新增這些所缺的內容;
         */
        Arrays.sort(users, (User o1, User o2) -> {
                return Integer.compare(o1.score,o2.score);
            }
        );
        System.out.println(Arrays.toString(users));
    }

    /**
     * 再次簡化lambda表示式
     */
    @Test
    public void testNewUse2(){
        User[] users = new User[]{new User("張三",86),new User("李四",87),new User("王五",88)};

        /* Lambda表示式
         * 第一:Integer.compare()這個方法會返回一個int值;
         * 第二:Comparetor這個介面的compare()方法也會返回一個int值;
         * 第三:JVM會自動推匯出Integer.compare()這個方法返回的int值即為compare()所要返回的值,所以return可以省略;
         */
        Arrays.sort(users, (User o1, User o2) -> Integer.compare(o1.score,o2.score));
        System.out.println(Arrays.toString(users));
    }

    /**
     * 最終簡化lambda表示式
     */
    @Test
    public void testNewUse3(){
        User[] users = new User[]{new User("張三",89),new User("李四",90),new User("王五",91)};

        /* Lambda表示式
         * 第一:如果程式碼塊中只有一行程式碼,
         * 第二:Integer.compare()這個方法會返回一個int值;
         * 第三:compare()這個方法也會返回一個int值,JVM會自動推匯出Integer.compare()這個方法返回的值為compare()的值,所以return可以省略;
         * 第四:compare()這個方法的引數型別一定是User型別,所以JVM會推匯出要比較兩個引數的型別,可以省略User
         */
        Arrays.sort(users, (o1,o2) -> Integer.compare(o1.score,o2.score));
        System.out.println(Arrays.toString(users));
    }
}

實踐:大家可以自行實現lambda表示式代替匿名內部類,建立Runnable介面例項,並重寫run()方法,然後啟動執行緒!

二、Lambda表示式之變數:(Lambda表示式俗稱閉包)

(1) 介面的方法引數:Lambda表示式中()圓括號中的引數變數;

(2) 區域性變數:Lambda表示式程式碼塊中宣告的變數;

(3) 自由變數:不是引數,也不是區域性變數,是外部方法的引數變數;

例2:匿名內部類使用外部變數,該變數一定是final修改的,不可改變

    //content、times即為自由變數
    public void repeatprint(String content,int times) {
        // 第一:匿名內部類若使用外部的變數,該變數一定是使用final修飾;
        Runnable runnable = () -> {   //若run()方法中有引數,即為介面的方法引數
            int i = 0;        //宣告一個區域性變數
            for (; i <= times; i++) {
                //content = "not update";    //該變數自動被final修飾,不能修改
                System.out.println(content);
            }
            //this指向的是外部類的例項,而不是內部類本身
            System.out.println(this);
        };
        new Thread(run).start();
    }

注意1:匿名內部類中的this指代匿名內部類本身,而Lambda表示式中的this指代的外部類例項,即為建立Lambda表表達式方法中的this(就是外部類本身);

注意2:Lambda表示式中訪問自由變數(建立Lambda表示式方法的引數),final修飾,不可修改! 

三、Lambda表示式之引數列表

(1) 如果Lambda表示式沒有引數,直接使用()來表示,()不能省略;

例3:啟動執行緒

//Lambda表示式沒有引數時,()不能省略,否則編譯出錯
public void testTread(){
        new Thread(() -> System.out.println("Hello lambda")).start();
}

(2) 如果Lambda表示式只有一個引數,若沒寫了引數型別,引數外可不加();若寫了引數型別,則引數外必須加();

例4:借用Frame中的Button元件新增事件處理:

        Frame f = new Frame();
        f.setSize(100,100);
        Button btn = new Button("lambda");
        //原始方式,匿名內部類建立
        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("Hello lambda");
            }
        });
        /* lambda表示式只有一個引數
         * 第一:addActionListener()這個方法的引數一定是ActionListener介面的實現類例項;
         * 第二:實現ActionListener介面一定要重寫actionPerformed()方法;
         * 第三:重寫actionPerformed()方法,引數一定是ActionEvent型別;
         * JVM會自動的幫助我們推匯出來
         */
        /* 第一種方式:btn.addActionListener((ActionEvent e) ->                     System.out.println("Hello lambda"));
         * 第二種方式:btn.addActionListener(event -> System.out.pringln("Hello lambda"));
         */
        f.add(btn);
        f.setVisible(true);

(3) 如果有兩個引數或多個引數,不管是否寫引數型別,一定要加();若引數要加修飾符,一定要新增完整的型別。

例5:將其字串陣列中升序排序

    /**
     * Lambda表示式有兩個引數
     */
    public void testLambda(){
        String[] strs = new String[]{"Android","IOS","Java","OS"};
        Arrays.sort(strs,(s1,s2) -> Integer.compare(s1.length(), s2.length()));
        /* 如果引數要加修飾符或者標籤(例如:final、static關鍵字),引數一定要加完整的型別;
         * Arrays.sort(strs,(final String s1,final String s2) -> Integer.compare(s1.length(), s2.length()));
         */
        System.out.println(Arrays.toString(strs));
    }

雖然例子簡單,但想理解Lambda表示式如何從匿名內部類轉變而來的原理,底層實現等,需要大家揣摩和思考。繼續歡迎大家參考:Java8新特性之Lambda表示式學習二