jdk8 Lambda表達式與匿名內部類比較


Labmda表達式與匿名內部類

前言

Java Labmda表達式的一個重要用法是簡化某些匿名內部類Anonymous Classes)的寫法。實際上Lambda表達式並不僅僅是匿名內部類的語法糖,JVM內部是通過invokedynamic指令來實現Lambda表達式的。具體原理放到下一篇。本篇我們首先感受一下使用Lambda表達式帶來的便利之處。

取代某些匿名內部類

本節將介紹如何使用Lambda表達式簡化匿名內部類的書寫,但Lambda表達式並不能取代所有的匿名內部類,只能用來取代函數接口(Functional Interface)的簡寫。先別在乎細節,看幾個例子再說。

例子1:無參函數的簡寫

如果需要新建一個線程,一種常見的寫法是這樣:

// JDK7 匿名內部類寫法
    class Anonymous {
        public void anonymousStyle() {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("Anonymous style thread run  ***************");
                }
            }).start();
        }
    }

上述代碼給Tread類傳遞了一個匿名的Runnable對象,重載Runnable接口的run()方法來實現相應邏輯。這是JDK7以及之前的常見寫法。匿名內部類省去了為類起名字的煩惱,但還是不夠簡化,在Java 8中可以簡化為如下形式:

class Lambda {
        public void lambdaStyle() {
            new Thread(() -> System.out.println("Lambda Style thread run  ******************")).start();
        }
    }

上述代碼跟匿名內部類的作用是一樣的,但比匿名內部類更進一步。這里連接口名和函數名都一同省掉了,寫起來更加神清氣爽。如果函數體有多行,可以用大括號括起來,就像這樣:

// JDK8 Lambda表達式代碼塊寫法
 class Lambda {
        public void lambdaStyle() {
            new Thread(() -> {
                System.out.println("lambda thread1 run********");
                System.out.println("lambda thread2 run********");
            }).start();
        }
    }

例子2:帶參函數的簡寫

如果要給一個字符串列表通過自定義比較器,按照字符串長度進行排序,Java 7的書寫形式如下:


    /**
     * JDK7 匿名內部類寫法
     */
    public void anonymousStyle() {
        List<String> names = Arrays.asList("cary", "jane", "jerry");
        Collections.sort(names, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o2.compareTo(o1);
            }
        });
        for (String name : names) {
            System.out.println("name:" + name);
        }
    }

上述代碼通過內部類重載了Comparator接口的compare()方法,實現比較邏輯。采用Lambda表達式可簡寫如下:

    /**
     * JDK8 Lambda表達式寫法
     */
    public void lambdaStyle() {
        List<String> names = Arrays.asList("cary", "jane", "jerry");
        Collections.sort(names, (o1, o2) -> {
            if (o1 == null)
                return -1;
            if (o1 == null)
                return 1;
            return o1.length() - o2.length();
        });
    }

上述代碼跟匿名內部類的作用是一樣的。除了省略了接口名和方法名,代碼中把參數表的類型也省略了。這得益於javac類型推斷機制,編譯器能夠根據上下文信息推斷出參數的類型,當然也有推斷失敗的時候,這時就需要手動指明參數類型了。注意,Java是強類型語言,每個變量和對象都必需有明確的類型。

簡寫的依據

也許你已經想到了,能夠使用Lambda的依據是必須有相應的函數接口(函數接口,是指內部只有一個抽象方法的接口)。這一點跟Java是強類型語言吻合,也就是說你並不能在代碼的任何地方任性的寫Lambda表達式。實際上Lambda的類型就是對應函數接口的類型Lambda表達式另一個依據是類型推斷機制,在上下文信息足夠的情況下,編譯器可以推斷出參數表的類型,而不需要顯式指名。Lambda表達更多合法的書寫形式如下:

/**
 * Created by cary on 2016/10/21.
 */
public class MyLambda {

    public void lambda() {
        Runnable run = () -> System.out.println("Thread run********");

        ActionListener listener = event -> System.out.println("button clicked");
        /**
         * 代碼塊
         */
        Runnable block = () -> {

            System.out.println("Lambda 代碼塊******");
            System.out.println("Lambda 代碼塊******");
        };
        BinaryOperator<Long> add = (Long x, Long y) -> x + y;
        /**
         * 5 類型推斷
         */
        BinaryOperator<Long> infer = (x, y) -> x + y;//
    }

    public static void main(String[] args) {
        new MyLambda().lambda();
    }
}

上述代碼中,1展示了無參函數的簡寫;2處展示了有參函數的簡寫,以及類型推斷機制;3是代碼塊的寫法;4和5再次展示了類型推斷機制。

參考文獻

  1. The Java® Language Specification
  2. http://viralpatel.net/blogs/lambda-expressions-java-tutorial/
  3. 《Java 8函數式編程 [英]沃伯頓》


免責聲明!

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



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