Java8 函數(shù)式接口 與 Lambda 及 方法引用

函數(shù)式接口

  • Lambda 表達式只能在函數(shù)式接口上操作,函數(shù)式接口是具有單個抽象方法的接口,也稱為 SAM(Single Abstract Method)
  • 函數(shù)接口可以有任意的 default 或 static 方法

函數(shù)式接口例子

  • 最基本的函數(shù)式接口
public interface Foo1 {
    void bar();
}
public interface Foo1 {
    int bar(boolean bz);
}
public interface Foo1 {
    String bar(Object baz, int mink);
}
  • default 方法不會被計算到函數(shù)式接口的單個抽象方法里
public interface Foo1 {
    default String bar() {          
        return "baz";
    }
    void quux();
}
  • 可以使用 @FunctionalInterface 注解,該注解可以讓編譯器幫我們審核該接口是否函數(shù)式接口
@FunctionalInterface
public interface Foo1 {
    void bar();
}
@FunctionalInterface
interface BlankFoo1 extends Foo1 {       // 從Foo1繼承抽象方法
}
@FunctionalInterface
interface Foo1 {
    void bar();
    boolean equals(Object obj);         // 重寫 Object 的方法因此不計算
}
  • 下面不是一個函數(shù)式接口,因為它沒有任何方法
@FunctionalInterface
interface Foo1 {}

Lambda 表達式

  • 任何函數(shù)的參數(shù)只要是一個函數(shù)式接口都能接收一個 Lambda
  • 案例準備:
public interface TestFunctionInterface1 {
    void print();
}
public interface TestFunctionInterface2 {
    void print(String name);
}
public interface TestFunctionInterface3 {
    void print(String name,int age);
}

// 與靜態(tài)方法

public static void print(TestFunctionInterface1 testFunctionInterface) {
        testFunctionInterface.print();
    }

public static void print(TestFunctionInterface2 testFunctionInterface) {
        testFunctionInterface.print("linyuan");
    }

public static void print(TestFunctionInterface3 testFunctionInterface) {
        testFunctionInterface.print("linyuan", 21);
    }
  • 通過 Lambda(“閉包”或“匿名方法”)表達式
print(()->System.out.println("Hello"));

// 等同于 

print(new TestFunctionInterface(){
    @Override
    public void print(){
        System.out.println("Hello");
    }
});
  • Lambda 表達式參數(shù)類型由編譯推理得出,也可以顯式的指定參數(shù)類型
Arrays.asList("a", "b", "c", "d").forEach((String e)  -> System.out.println(e));
  • Lambda 表達式是匿名函數(shù),因為函數(shù)式接口有一個抽象方法,所以 Java 重寫了它,若 Lambda 類型不能確定,需要加上強制類型轉(zhuǎn)換
TestFunctionInterface1 t1 = (TestFunctionInterface1)()->System.out.println("hello");
testFunctionInterface.print();
  • 如果函數(shù)只有一個參數(shù),可以省略圓括號,并且如果代碼比較多,可以使用花括號括起來:
TestFunctionInterface2 t2 = name -> {System.out.println("my name is "+name);};
TestFunctionInterface3 t3 = (name,age) -> {System.out.println("my name is "+name+" and age "+age);};
  • 如果Lambda中的代碼是一個表達式而不是一個聲明,它就會被當作一個返回值
IntUnaryOperator addOne = x -> x + 1;
  • 使用 Lambda 表達式去排序一個集合,在 Java8 之前,排序一個集合的時候,需要用匿名內(nèi)部類去實現(xiàn) java.util.Comparator 接口:
// 匿名內(nèi)部類方式:
List<String> list = Arrays.asList("a", "c", "b");
list.stream().sorted(new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
        return o1.compareTo(o2);
    }
});

// Lambda 表達式方式:
List<String> list = Arrays.asList("a", "c", "b");
list.stream().sorted((p1,p2)->p1.compareTo(p2));
  • Lambda 表達式(匿名類) 在訪問外部局部變量時,其實是拷貝了一份外部局部變量的副本,修改副本的值是不會影響到外部局部變量的值的(因為Java只有值拷貝,沒有引用拷貝,而外部局部變量的引用與Lambda表達式內(nèi)部變量的引用并非同一個),因此若欲想在 Lambda 表達式中修改外部局部變量的值,則會提醒需要將外部局部變量顯式的聲明為 final,以免開發(fā)者會誤以為能夠在 Lambda 表達式中修改外部局部變量的值
int number = 0;
IntStream.range(1, 3).forEach(i -> {
    // 若無修改外部局部變量的值的操作,則無需聲明為 final
    System.out.println(number);
});
int number = 0; 
IntStream.range(1, 3).forEach(i -> {
    // 此處報錯,Lambda 表達式中不能修改外部局部變量的值
    // 因為 Lambda 表達式中的變量是外部局部變量的副本拷貝,修改副本的值不會影響原變量的值
    // 需顯示的聲明 number 為 final
    number = number + 1;       
});

方法引用

  • 方法引用是用來直接訪問類或者實例已存在的方法或構(gòu)造方法,可以理解為 Lambda 簡化寫法,使用 :: 來引用
@FunctionalInterface
public interface Supplier<T> {
    T get();
}
 
class Car {
    //Supplier是jdk1.8的接口,這里和lamda一起使用了
    public static Car create(final Supplier<Car> supplier) {
        return supplier.get();
    }
 
    public static void collide(final Car car) {
        System.out.println("Collided " + car.toString());
    }
 
    public void follow(final Car another) {
        System.out.println("Following the " + another.toString());
    }
 
    public void repair() {
        System.out.println("Repaired " + this.toString());
    }
}
  • 構(gòu)造器引用
Car car = Car.create( Car::new );
List< Car > cars = Arrays.asList( car );
  • 靜態(tài)方法引用
cars.forEach( Car::collide );
  • 特定類任意對象方法引用
cars.forEach( Car::repair );
  • 特定對象的方法引用
Car police = Car.create( Car::new );
cars.forEach( police::follow );
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容