方法引用(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,這些用在方法引用還不錯
沒有留言:
張貼留言