方法引用(Method Reference)的符號是「::」,如果方法體已經寫好了,就可以使用
它的功能和上一章的 lambda類似,但沒辦法加工,而 lambda 裡面還可以寫引用
※方法引用
import java.util.Arrays; import java.io.PrintStream; @FunctionalInterface interface I1 { void fi(String s); } @FunctionalInterface interface I2 { void fi(int... i); } @FunctionalInterface interface I3 { public String fi(int p); } @FunctionalInterface interface I4 { void fi(PrintStream out, String s); } public class Java8Test { public static void main(String[] args) { I1 i = (c) -> System.out.println(c); i.fi("lambda"); I1 i1 = System.out::println; i1.fi("引用1"); ((I1) System.out::println).fi("引用2"); int[] iArray = new int[] { 3, 1, 52, 4 }; System.out.println("before:" + Arrays.toString(iArray)); // java7(含)之前的做法 // Arrays.sort(iArray); // 使用標準的lambda I2 i2a = (x) -> Arrays.sort(x); i2a.fi(iArray); // 使用引用 // I2 i2b = Arrays::sort; // i2b.fi(iArray); System.out.println("after:" + Arrays.toString(iArray)); // 普通方法(和上面的System.out::println一樣的意思) String str = "12345"; I3 i3a = (x) -> { return str.substring(x); }; System.out.println(i3a.fi(2)); I3 i3b = new String("12345")::substring; System.out.println(i3b.fi(2)); // 參數是物件 I4 i4a = (x, o) -> x.println(o); i4a.fi(System.out, "i4a"); I4 i4b = PrintStream::println; i4b.fi(System.out, "i4b"); } }
※使用引用也是可以強制轉換後寫成一行
※靜態方法可以將「.」變成「::」,然後後面的括號都不寫
※普通方法先宣告好或用建構子寫在::的左邊
※要試無參數和無回傳值,可用 Map 的 clear
※引用加泛型
P表示參數;R表示回傳值import java.util.Arrays; import java.io.PrintStream; @FunctionalInterface interface I1<P> { void fi(P s); } @FunctionalInterface interface I2<P> { void fi(P i); } @FunctionalInterface interface I3<P, R> { public R fi(P p); } @FunctionalInterface interface I4<P1, P2> { void fi(P1 p1, P2 p2); } public class Java8Test { public static void main(String[] args) { I1<String> i = (c) -> System.out.println(c); i.fi("lambda"); I1<String> i1 = System.out::println; i1.fi("引用1"); ((I1<String>) System.out::println).fi("引用2"); int[] iArray = new int[] { 3, 1, 52, 4 }; System.out.println("before:" + Arrays.toString(iArray)); // java7(含)之前的做法 // Arrays.sort(iArray); // 使用標準的lambda // I2<int[]> i2a = (x) -> Arrays.sort(x); // i2a.fi(iArray); // 使用引用 I2<int[]> i2b = Arrays::sort; i2b.fi(iArray); System.out.println("after:" + Arrays.toString(iArray)); // 普通方法(和上面的System.out::println一樣的意思) String str = "12345"; I3<Integer, String> i3a = (x) -> { return new String("12345").substring(x); }; System.out.println(i3a.fi(2)); I3<Integer, String> i3b = str::substring; System.out.println(i3b.fi(2)); // 參數是物件 I4<PrintStream, String> i4a = (x, o) -> x.println(o); i4a.fi(System.out, "i4a"); I4<PrintStream, String> i4b = PrintStream::println; i4b.fi(System.out, "i4b"); } }
※
※建構子
@FunctionalInterface interface I1 { ConstructorTest fi(); } @FunctionalInterface interface I2 { ConstructorTest fi(String s); } class ConstructorTest { ConstructorTest() { System.out.println("無參"); } ConstructorTest(String s) { System.out.println("有參" + s); } } public class Java8Test { public static void main(String[] args) { I1 i1a = () -> new ConstructorTest(); i1a.fi(); I1 i1b = ConstructorTest::new; i1b.fi(); I2 i2a = (x) -> new ConstructorTest(x); i2a.fi("xxx"); I2 i2b = ConstructorTest::new; i2b.fi("xxx"); } }
※
※建構子加泛型
@FunctionalInterface interface I1<R extends ConstructorTest> { R fi(); } @FunctionalInterface interface I2<R extends ConstructorTest> { R fi(String s); } class ConstructorTest { ConstructorTest() { System.out.println("無參"); } ConstructorTest(String s) { System.out.println("有參" + s); } } public class Java8Test { public static void main(String[] args) { I1<ConstructorTest> i1a = () -> new ConstructorTest(); System.out.println(i1a.fi()); I1<ConstructorTest> i1b = ConstructorTest::new; System.out.println(i1b.fi()); I2<ConstructorTest> i2a = (x) -> new ConstructorTest(x); System.out.println(i2a.fi("xxx")); I2<ConstructorTest> i2b = ConstructorTest::new; System.out.println(i2b.fi("xxx")); } }
※塞值可以參考 反射(一) 塞值的部分
※加泛型注意事項
interface 裡的 方法,不可以用泛型方法,否則使用時會編譯錯誤 Target method is generic,就不能用 lambda 了,只能用匿名類別了
例:
@FunctionalInterface interface Animal { int get(Monkey m); // 如果在此宣告泛型方法,這裡可以編譯,但 main 方法裡的 a -> 1 會報錯 } @Data @AllArgsConstructor class Monkey { private int id; private String name; public static void main(String[] args) { Animal b = a -> 1; b.get(new Monkey(999, "monkey")); } }
※包含::方法名
總共有四種引用方式1.建構子,很簡單,看了就會
2.靜態,一樣看了就會
3.因為靜態可以直接去「.」,不是靜態就只好用 new 的方式,然後一樣接「::」
4.不是靜態,但看起來像靜態的方式,這種方式,第一個參數一定要傳「::」左邊的物件才行
Function<Integer, String> i3b = new String("0123456789")::substring; System.out.println(i3b.apply(4)); Function<char[], String> i3ba = String::copyValueOf; System.out.println(i3ba.apply(new char[] { 'a', 'b', 'c', 'd', 'e' })); BiFunction<String, Integer, String> i4 = String::substring; System.out.println(i4.apply("0123456789", 4)); Supplier<J8Test> con1 = J8Test::new; con1.get(); Function<String, J8Test> con2 = J8Test::new; con2.apply("oo");
※想法要先寫引用再想方法名怎麼寫,會比較好理解
※官方網站的例子比較好理解了吧!
※範例
@FunctionalInterface public interface Ixxx { int method(Xxx x, String s, int i); public static void main(String[] args) { // 引用 Ixxx x = Xxx::xxxMethod; System.out.println(x.method(new Xxx(9), "5", 1)); // Lambda Ixxx o = (xxx, y, z) -> xxx.xxxMethod(y, z); System.out.println(o.method(new Xxx(9), "5", 1)); } } -------------------- public class Xxx { private int a; public Xxx(int a){ this.a = a; } public int xxxMethod(String s, int i){ return this.a + Integer.parseInt(s) + i; } }
※注意 Ixxx 的 method 方法和 Xxx 的 xxxMethod 裡的參數有何不同
※另外一種記法是以 Lambda 寫出來後,如上的 (xxx, y, z) -> xxx.xxxMethod(y, z)
第一個參數去點方法,而這個方法的參數是第二個之後的參數,且是有照順序的,此時可用方法引用
※1.8 有在 Integer、Long、Float、Double 增加 靜態的 sum、max、min,這些用在方法引用還不錯
沒有留言:
張貼留言