2016年12月10日 星期六

@FunctionalInterface (java8 二)

※@FunctionalInterface

java8在介面上可加這個annotation,表示裡面一定只能剛好有一個未實作的method,不能少也不能多(連繼承的也算),但覆寫 Object 的 public 方法不會納入計算,驗證沒過都會報「InterfaceName is not a functional interface」的錯,但default、static沒有關係

不加上這個參數也是可以,和@Override一樣,有加的話,錯誤可以在編譯期就發現


※無參數無回傳值

@FunctionalInterface
interface I1 {
    void fi();
}
    
public class Java8Test {
    public static void main(String[] args) {
        I1 test = new I1() {
            @Override
            public void fi() {
                System.out.println("使用匿名1");
            }
        };
        test.fi();
    
        new I1() {
            @Override
            public void fi() {
                System.out.println("使用匿名2");
            }
        }.fi();
    
        I1 lambda1 = () -> {
            System.out.println("使用Lambda-1");
        };
        lambda1.fi();
    
        /*
         * 不可合併成: 
         * () -> { System.out.println("使用Lambda-1"); }.fi();
         * 因為 {} 還不知道要 assign 給哪一個介面,但可以強轉,下一個例子有
         */
    
        I1 lambda2 = () -> System.out.println("使用Lambda-2");
        lambda2.fi();
    }
}

※匿名類別在編譯成.class時,會多一個「ClassName$1.class」檔案,但lambda不會產生

※():參數,如果介面上定義的是int,那就要寫(int i),不能寫Integer,編譯不會過,但類型可以省略不寫
->:固定的語法
{}:實作的內容

※() -> {}把它想成在寫匿名類別,然後用介面唯一的方法呼叫


※強轉

@FunctionalInterface
interface I1 {
    void fi();
}
    
public class Java8Test {
    public static void main(String[] args) {
        ((I1) () -> {
            System.out.println("xxx");
        }).fi();
    }
}




※其他的參數和回傳值

@FunctionalInterface
interface I1 {
    void fi(int i);
}
    
@FunctionalInterface
interface I2 {
    void fi(int i, int j);
}
    
@FunctionalInterface
interface I3 {
    String fi();
}
    
@FunctionalInterface
interface I4 {
    int fi(int i, int j);
}
    
public class Java8Test {
    public static void main(String[] args) {
        I1 lambda1a = (x) -> {
            System.out.println("I1a=" + x);
        };
        lambda1a.fi(1);
    
        I1 lambda1b = x -> System.out.println("I1b=" + x);
        lambda1b.fi(1);
    
        I2 lambda2a = (x, o) -> {
            System.out.println("I2a=" + x + ", " + o);
        };
        lambda2a.fi(1, 2);
    
        I2 lambda2b = (x, o) -> System.out.println("I2b=" + x + ", " + o);
        lambda2b.fi(1, 2);
    
        I3 lambda3a = () -> {
            return "call success";
        };
        System.out.println("I3a=" + lambda3a.fi());
    
        // 沒有「{}」時,連「return」關鍵字也不能有
        I3 lambda3b = () -> "call success";
        System.out.println("I3b=" + lambda3b.fi());
    
        I4 lambda4a = (x, o) -> {
            return x + o;
        };
        System.out.println("I4a=" + lambda4a.fi(1, 2));
    
        I4 lambda4b = (x, o) -> x + o;
        System.out.println("I4b=" + lambda4b.fi(1, 2));
    }
}

※結果:
I1a=1
I1b=1
I2a=1, 2
I2b=1, 2
I3a=call success
I3b=call success
I4a=3
I4b=3

※參數的型態可省略

※有b的都是簡便寫法,參數只有一個時,可以省略圓括號

※如果只有一行時,可以省略「{}」,而且如果有回傳值,連 return 關鍵字也不能有

※圖括號裡的參數是隨便打的,呼叫時,如lambda1a.fi(1),此時會將1傳給x


※介面為參數

@FunctionalInterface
interface I1 {
    void fi();
}
    
public class Java8Test {
    public void xxx(I1 i1) {
        System.out.println(i1);
        i1.fi();
    }
    
    public static void main(String[] args) {
        new Java8Test().xxx(new I1() {
            @Override
            public void fi() {
                System.out.println("使用匿名" + System.getProperty("line.separator"));
            }
        });
    
        new Java8Test().xxx(() -> System.out.println("使用Lambda"));
    }
}

※結果:
Java8Test$1@2a139a55
使用匿名

Java8Test$$Lambda$1/834600351@548c4f57
使用Lambda

和匿名比較起來,會在類名後加上「$$Lambda」,當然匿名還是會產生class,而lambda還是不會產生class


※使用外部變數


int i = 0;
I1 ia = new I1() {
    int j = 0;
    
    @Override
    public void m() {
        System.out.println(i);
        System.out.println(++j);
    }
};
ia.m();
    
int k = 0;
I1 ib = () -> {
    int l = 0;
    System.out.println(k);
    System.out.println(++l);
};
ib.m();

※上面是匿名,下面是lambda
1.7(含) 之前,i 變數必需是 final,否則編譯錯誤
但 1.8 可不用寫 final 了,但如果你想改變 i 的值,假設 ++i,還是會編譯錯誤,也就是還是 final
相同的道理,lambda 也會有一樣的問題,指的是 i 和 k 變數,不是 j 和 l 變數

沒有留言:

張貼留言