2/27/2014
2:31:00 PM 0

Java 8 雙冒號 Method Reference

Java 8 加入了 lambda expression 賦予了 Java 新的能力, 在使用 lambda 的同時常發現神奇的 :: 雙冒號 (double colon) 穿插其中, :: 代表什麼意義呢?

舉例來說, 在過去我們要做個由小到大的數列排序,當然使用 java.util.Arrays.sort(Object[] a) 是最快的方式, 如果要自訂排序方式使用 anonymous inner class 是最簡便的方式

例 1 : 使用 anonymous inner class
Integer[] ary = new Integer[] { 9, 8, 7, 6, 5, 4, 3, 2, 1 };

// anonymous inner class
Arrays.sort(ary, new Comparator() {
    @Override
    public int compare(Integer a, Integer b) {
        return Integer.compare(a, b);
    }
});

System.out.println(Arrays.toString(ary));
Java 8 之後你可以這樣寫

例 2 : 使用 lambda expression
Integer[] ary = new Integer[] { 9, 8, 7, 6, 5, 4, 3, 2, 1 };

// lambda expression
Arrays.sort(ary, (a, b) -> Integer.compare(a, b));

System.out.println(Arrays.toString(ary));
是不是簡潔了很多呢? 但是我們發現 (a, b) -> Integer.compare(a, b)), 傳入兩個 Integer parameter 再 return Integer 不就跟 Integer.compare 一樣嗎? (a, b) -> 感覺好像是多餘的,因此有更精簡的做法, 就是使用 lambda expression 的 method reference

例 3 : 使用 method reference
Integer[] ary = new Integer[] { 9, 8, 7, 6, 5, 4, 3, 2, 1 };

// method reference
Arrays.sort(ary, Integer::compare);

System.out.println(Arrays.toString(ary));

:: 雙冒號 (double colon) 是 Java 8 加入的新 operator, 稱作 Method Reference, Method Reference 提供式四個種類的用法

1. Reference to a static method
前面例3 Arrays.sort(ary, Integer::compare) 就屬此類, Integer.compare 是 static method

2. Reference to an instance method of a particular object
使用 instance method, 傳入參數與回傳型別必須與 interface 的 compare method 定義一致
class MyCompare {
    public int compareInt(int a, int b) {
        return a - b;
    }
}

Integer[] ary = new Integer[] { 9, 8, 7, 6, 5, 4, 3, 2, 1 };

MyCompare my = new MyCompare();
Arrays.sort(ary, my::compareInt);

System.out.println(Arrays.toString(ary));
3. Reference to an instance method of an arbitrary object of a particular type
使用 instance method, 與前例比較原本是傳入兩個參數 (int a, int b) 回傳一個 int, 轉換為 a.compareTo(b) 的形式, 同樣都回傳 int
Integer[] ary = new Integer[] { 9, 8, 7, 6, 5, 4, 3, 2, 1 };

Arrays.sort(ary, Integer::compareTo);

System.out.println(Arrays.toString(ary));
4. Reference to a Constructor
Reference to a Constructor 相較其他三個比較複雜些,用下面的例子來說明,首先創建 ICarFactory interface 和 CarBean class
interface ICarFactory {
    CarBean getCar(String color, int year);
}

class CarBean {
    private String name;
    private int maxSpeed;

    CarBean(String name, int maxSpeed) {
        this.name = name;
        this.maxSpeed = maxSpeed;
    }

    String getName() {
        return name;
    }

    int getMaxSpeed() {
        return maxSpeed;
    }
}
要實作 ICarFactory, 傳統的方法是使用 "implements" 關鍵字來實作介面

Java 8 可使用 lambda expression, 實作可改寫成如下的方式,簡潔許多
ICarFactory f = (String name, int maxSpeed) -> { return new CarBean(name, maxSpeed); };
仔細觀察可發現,Interface 所定義的 getCar(String color, int year) 和 CarBean(String name, int maxSpeed) 傳入參數型別正好一樣,因此可使用 method reference => Reference to a Constructor 的方式,再把寫法簡化
ICarFactory f = CarBean::new;
測試 Reference to a Constructor
//ICarFactory f = (String name, int maxSpeed) -> { return new CarBean(name, maxSpeed); };
ICarFactory f = CarBean::new;
CarBean c = f.getCar("GTR", 300);
System.out.println("Car Name:" + c.getName() + " / Max Speed:" + c.getMaxSpeed());
使用 lambda 或 method reference 可有效的讓程式碼更簡短,也提供程式更大的彈性,不管用以上哪一種寫法都是由編譯器來做推斷和解釋完成,這是使用上需要留意的部分

0 comments:

Post a Comment