如果一個Java
介面類包含且僅包含一個無預設實現的方法定義,那麼他被稱為函數式介面。這個方法定義了介面的預期用途。
@FunctionalInterface
註解指示一個介面類是一個函數介面,不允許有多個沒有預設實現的介面,但不強制要求在介面上新增該註解
範例1:定義一個函數式介面
@FunctionalInterface
public interface MyFunctionalInterface {
// the single abstract method
void function();
}
範例1中有且僅有沒有預設實現的方法定義——function()
, 因此它是一個功能介面。
lambda
表示式本質上是一個匿名函數,用於實現一個函數介面定義的功能(不能理解的話可以看作是一個匿名物件,後面會專門說區別)。
lambda
表示式的一般語法是:
(Parameters) -> { Body }
Parameters
為函數的入參,放在()
中,與方法定義相同,lambda
表示式的實現放在{}
中
注意
lambda
表示式稱為顯示lambda
表示式,反之稱之為隱式lambda
表示式()
是可選的,當引數有且僅有一個時,可以省略小括號,否則必須保留{}
是可選的,如果實現的主題只有一條語句,則可以省略,反之必須保留retuen
是可選的,在省略{}
的同時,如果表示式有返回值可以省略關鍵字retuen
範例2:
// 給定一個值x,返回其的3倍
(int x) ->{return x*3;};
// 省略可選項後定義如下:
x -> x * 3;
可以將lambda
表示式作為引數傳遞給方,就像傳遞一個普通的物件一樣
範例3:
public class Test {
public static void main(String[] argv) {
doWork((x,y)-> x + y, 4, 2);
}
// (x,y)-> x + y 即為Function 函數介面的實現
private static int doWork(Function function, int x, int y){
return function.cal(x,y);
}
// 定義一個函數介面, 對兩個整形進行計算
@FunctionalInterface
interface Function{
int cal(int x, int y);
}
}
lambda
表示式使用時不會生成新的物件,也就不會產生自己的範圍。
如果lambda
表示式中使用關鍵字this
和super
,他的行為與在執行lambda
表示式的方法中使用this
和super
的行為完全相同。
範例4:
public class Test {
public static void main(String[] argv) {
Test test = new Test();
test.test();
}
private void test(){
MyFunctionalInterface functionNewClass = new MyFunctionalInterface(){
@Override
public void function(int x) {
System.out.println("匿名內部類this======= "+ this);
}
};
MyFunctionalInterface functionLambda = x -> System.out.println("lambda表示式中執行this ========== " + this);
System.out.println("方法中執行this======= "+ this);
functionNewClass.function(1);
functionLambda.function(1);
}
}
// 執行結果
方法中執行this======= cn.bmilk.Test@4bf558aa
匿名內部類this======= cn.bmilk.Test$1@2d38eb89
lambda表示式中執行this ========== cn.bmilk.Test@4bf558aa
範例3的結果顯示,lambda
表示式中呼叫this
指標和在方法test()
中呼叫this
指代的是同一個物件cn.bmilk.Test@4bf558aa
, 而使用匿名內部類this
指代的是當前匿名內部類產生的物件cn.bmilk.Test$1@2d38eb89
,所以lambda表示式和匿名內部類是不同的
lambda
表示式可以使用`標記了`final
的外層區域性變數,這就是說在lambda
表示式中不能修改外層定義的區域性變數(不包含全域性變數),否則會編譯錯誤。
範例5:參照外層區域性變數
private void test(){
int num = 2;
MyFunctionalInterface functionLambda = x -> System.out.println(x * num);
functionLambda.function(1);
}
範例5:
test*()
中定義了一個變數num
,並且在functionLambda
中進行了參照。前面提到lambda
表示式使用外層區域性變數,外層區域性變數需要定義為final
,但這裡並沒又定義為final
,這是因為num
雖然沒有被定義為final
,但是在num
被定義之後並沒有再次改變,看起來和一個被final
修飾的變數一樣,即含有隱性的final
定義。
當嘗試再後面修改num
的值,編譯器會提示錯誤資訊,如範例6
範例6:
private void test(){
int num = 2;
num = 3;
// 編譯器提示 參照外層區域性變數,外層區域性變數必須為final或者等同於final
// Variable used in lambda expression should be final or effectively final
MyFunctionalInterface functionLambda = x -> System.out.println(x * num);
functionLambda.function(1);
}
之所以參照外層變數必須是
final
是因為,區域性變數被定義再棧幀中,java
存取區域性變數的時候實際上是存取他的副本,多個執行緒對應了多個棧,當lambda
表示式執行執行緒與定義執行緒不是同一個情況下,如果變數不被定義為final
,當一個執行緒改變了變數值,另一個有可能會讀取不到最新值導致出錯。
java8
及更高版本內建了一系列的函數式介面,被放在java.util.function
包下,用於支援j函數語言程式設計。另外在之前的版本中存在的java.lang.Runnable
、java.util.Comparator
也都可以用於函數語言程式設計