<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 495,comments - 227,trackbacks - 0
    http://unmi.cc/jdk8-lambda-method-references/

    Lambda 允許我們定義匿名方法(即那個 Lambda 表達式,或叫閉包),作為一個功能性接口的實例。如果你不想把一個 Lambda 表達式寫得過大,那么你可以把表達式的內容分離出來寫在一個方法中,然后在放置 Lambda 表達式的位置上填上對那個方法的引用。

    方法引用也應看作是一個 Lambda 表達式,所以它也需要一個明確的目標類型充當功能性接口的實例。簡單說就是被引用的方法要與功能接口的 SAM(Single Abstract Method) 參數、返回類型相匹配。方法引用的引入避免了 Lambda 寫復雜了可讀性的問題,也使得邏輯更清晰。

    為了應對方法引用這一概念, JDK8 又重新借用了 C++ 的那個 “::” 域操作符,全稱為作用域解析操作符。

    上面的表述也許不好明白,我看官方的那份 State of the Lambda 也覺得不怎么容易理解,特別是它舉了那個例子很難讓人望文生意。我用個自己寫的例子來說明一下吧。

    目前的 Eclipse-JDK8 版還不能支持方法引用的特性,幸好就是在昨天正式版的 NetBeans IDE 7.4 對 JDK8 有了較好的支持,所以在 NetBeans 7.4 中寫測試代碼。

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    package testjdk8;
     
    /**
     *
     * @author Unmi
     */
    public class TestJdk8 {
     
        public static void main(String[] args) {
            //使用 Lambda 表達式,輸出: 16: send email
            start((id, task) -> id + ": " + task);
            //或者
            Machine m1 = (id, task) -> id + ": " + task;
            m1.doSomething(16, "send email");
             
            //使用方法引用,輸出: Hello 16: send email
            start(TestJdk8::hello);
            //或者
            Machine m2 = TestJdk8::hello;
            m2.doSomething(16, "send email");
        }
         
        private static void start(Machine machine){
            String result = machine.doSomething(16, "send email");
            System.out.println(result);
        }
         
        public static String hello(int id, String task){
            return "Hello " + id +": " + task;
        }
    }
     
    @FunctionalInterface
    interface Machine {
        public String doSomething(int id, String task);
    }

    說明:

    1. Machine 是一個功能性接口,它只有一個抽象方法
    2. start(Machine machine) 方法為 Lambda 表達式提供了一個上下文,表明它期盼接收一個 Machine 的功能性接口類型
    3. start((id, task) -> id + ": " + task), 是傳遞了一個 Lambda 表達式給 start() 方法
    4. start(TestJdk8::hello) 是把指向 TestJdk8::hello 方法的引用傳遞給了 start() 方法,這里可以理解 hello() 方法是 Lambda 表達式的另一種表現形式。

    對應一下兩個 start() 方法調用的參數,Lambda 表達式的參數列表 (id, task) 與 hello 方法的參數 (int id, String task) 是一致的,返回值類型也是一致的。

    想像一下如果一個 Lambda 表達式的代碼量很大,全部擠在一起作為 start() 方法的參數部分,混亂也不太方便于單步調試。所以可以把 Lambda 的實現挪出來放在一個單獨的方法中,在使用處只放置一個對該方法的引用即可。借助于方法引用,JDK8  把方法與 Lambda 表達式巧妙的結合了起來,直接的說 Lambda 表達就是一個方法,它用自己的方法列表和返回值。

    那么符合什么條件的方法可以作為 Lambda 表達式來用呢?答:方法簽名與功能性接口的 SAM 一致即可。比如,可以進行下面的賦值:

    1
    2
    Consumer<Integer> b1 = System::exit //void exit(int status) 與 Consumer 的 SAM void accept(T t) 相匹配
    Runnable r = MyProgram::main;      //void main(String... args) 與 run() 方法能配上對

    有些什么樣子的方法引用:

    1. 靜態方法 (ClassName::methName)
    2. 對象的實例方法 (instanceRef::methName)
    3. 對象的super 方法 (super::methName)
    4. 類型的實例方法 (ClassName::methName, 引用時和靜態方法是一樣的,但這里的 methName 是個實例方法)
    5. 類的構造方法 (ClassName::new)
    6. 數組的構造方法 (TypeName[]::new)

    第 1 條,靜態方法以 ClassName 為作用域好理解,第 4 條中實例方法也可以用 ClassName::methName 的方式去引用,那么這里又有個約定了:如果實例方法用類型來引用的時候,那么調用時第一個參數將作為該引用方法的接收者,其余參數依次作為引用方法的參 數。舉個例子:

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    package testjdk8;
     
    import java.util.function.Function;
     
    /**
     *
     * @author Unmi
     */
    public class TestJdk8 {
     
        public static void main(String[] args) {
            Function<String, String> upperfier = String::toUpperCase;
            System.out.println(upperfier.apply("Hello")); //HELLO
             
            Machine m = TestJdk8::hello; //hello 是實例方法
            TestJdk8 test = new TestJdk8();
            //test 作為 hello 方法的接收者,"Unmi" 作為 task 參數
            System.out.println(m.doSomething(test, "Unmi")); //Hello Unmi
        }
         
        public String hello(String task){
            return "Hello " + task;
        }
    }
     
    @FunctionalInterface
    interface Machine {
        public String doSomething(TestJdk8 test, String task);
    }

    上面的代碼應該能有助于理解實例方法用類型來引用,如果引用的是泛型方法,類型寫在 :: 之前。

    同樣當然對于第 2 條,引用實例方法時,SAM 的第一個參數也作為接收者,其作參數依次填充過去。

    第 5 條,類的構造方法要用類型去引用,new 相當一個返回當前類型實例的實例方法,所以

    1
    2
    SocketImplFactory factory = MySocketImpl::new;
    SocketImpl socketImpl = factory.createSocketImpl();

    數組是種類型,可以認為數組的構造方法是只接受一個整形參數,所以能這樣引用數組的構造方法:

    1
    2
    IntFunction<int[]> arrayMaker = int[]::new;
    int[] array = arrayMaker.apply(10);  // creates an int[10]
    小結:Lambda 表達式就是一個功能性接口的實例,因而調用方式參照功能性接口。Lambda 表達式可抽取到一個方法中,然后用方法引用指向這個方法,被引用的方法簽名與功能性接口的 SAM 的一致性,注意引用實例方法時,SAM 的第一個參數將作為引用方法的接收者。我們把數組理解為有一個接收整數的構造方法。
    posted on 2014-12-25 16:08 SIMONE 閱讀(2408) 評論(0)  編輯  收藏 所屬分類: JAVA
    主站蜘蛛池模板: 国产免费一区二区三区VR| 亚洲不卡av不卡一区二区| 人体大胆做受免费视频| 亚洲人成在线播放网站| 麻豆最新国产剧情AV原创免费| 无码亚洲成a人在线观看| 中文字幕不卡亚洲 | 久久久久久毛片免费播放| 亚洲综合色一区二区三区| 国产亚洲精品无码拍拍拍色欲| 成人免费激情视频| 人体大胆做受免费视频| 亚洲日韩国产精品乱-久| 国产av无码专区亚洲av果冻传媒| 麻豆国产精品免费视频| a级毛片免费观看网站| 亚洲国产日韩在线| 国产午夜亚洲不卡| 免费鲁丝片一级在线观看| 国产成人AV片无码免费| 男男gay做爽爽的视频免费| 亚洲国产成人无码av在线播放| 亚洲乱码国产一区网址| 成人毛片18岁女人毛片免费看| 97国免费在线视频| 大桥未久亚洲无av码在线| 亚洲综合视频在线观看| 狠狠亚洲狠狠欧洲2019| 日本特黄特色免费大片| 免费成人福利视频| 久久大香香蕉国产免费网站 | 和日本免费不卡在线v| 国产免费一区二区三区不卡 | 69国产精品视频免费| 五月婷婷免费视频| 亚洲欧美日本韩国| 亚洲性无码av在线| 亚洲A∨无码一区二区三区| 久久亚洲高清综合| www.亚洲精品.com| 国产网站在线免费观看|