函數式接口(Functional interface)
就是一個且僅有一個抽象方法,但是可以有多個非抽象方法的接口。
函數式接口可以被隐式轉換為lambda表達式。
如果定義一個函數式接口如下:
interface GreetingService
{
void sayMessage(String message);
}
那麼就可以使用Lambda表達式來辨別該接口的一個實作,(Java8之前一般用匿名類實作),Lambda表達式免去了使用内部匿名類的麻煩,給予Java簡單強大的函數化程式設計能力。
lambda表達式文法:
(parameters) -> expression
注意:
1無需指定lambda表達式的傳回類型。lambda表達式的傳回類型總是會由上下文推導得出。
2 如果一個lambda表達式隻在某些分支傳回一個值,而在另一些分支不傳回值,這是不合法的。
例如:
一些可以簡化的寫法:
可選的類型聲明
可選的參數圓括号
可選的大括号
可選的傳回關鍵字
函數式接口+Lambda表達式:
public class Java8Tester {
public static void main(String args[]){
Java8Tester tester = new Java8Tester();
// 類型聲明
MathOperation addition = (int a, int b) -> a + b;
// 不用類型聲明
MathOperation subtraction = (a, b) -> a - b;
// 大括号中的傳回語句
MathOperation multiplication = (int a, int b) -> { return a * b; };
// 沒有大括号及傳回語句
MathOperation division = (int a, int b) -> a / b;
System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
System.out.println("10 / 5 = " + tester.operate(10, 5, division));
// 不用括号
GreetingService greetService1 = message ->
System.out.println("Hello " + message);
// 用括号
GreetingService greetService2 = (message) ->
System.out.println("Hello " + message);
greetService1.sayMessage("Runoob");
greetService2.sayMessage("Google");
}
interface MathOperation {
int operation(int a, int b);
}
interface GreetingService {
void sayMessage(String message);
}
private int operate(int a, int b, MathOperation mathOperation){
return mathOperation.operation(a, b);
}
}
//代碼轉載 LovelyBear2019的部落格
變量作用域:
Lambda表達式隻能引用标記了final的外層局部變量,不能再lambda内部修改定義在域外的局部變量,否則會編譯錯誤。
public class Java8Tester {
final static String salutation = "Hello! ";
public static void main(String args[]){
GreetingService greetService1 = message ->
System.out.println(salutation + message);
greetService1.sayMessage("Runoob");
}
interface GreetingService {
void sayMessage(String message);
}
}
也可以直接在lambda表達式中通路外層的局部變量:
public class Java8Tester {
public static void main(String args[]) {
final int num = 1;
Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
s.convert(2); // 輸出結果為 3
}
public interface Converter<T1, T2> {
void convert(int i);
}
}
lambda表達式通路的局部變量可以不用聲明為final,但是必須不可被後面的代碼修改。
int num = 1;
Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
s.convert(2);
num = 5;
//報錯資訊:Local variable num defined in an enclosing scope must be final or effectively
final
Lambda表達式當中不允許聲明一個與局部變量同名的參數or局部變量
String first = "";
Comparator<String> comparator = (first, second) -> Integer.compare(first.length(), second.length()); //編譯會出錯
方法引用
可能已經有現成方法可以完成你想要傳遞到其他代碼的某個動作。
System.out::println //是一個方法引用(method reference) 它等價于lambda表達式
x->System.out.println(x);
Arrays.sort(strings,String::compareToIgnoreCase)
使用::操作符 分割 方法名 與對象or類名
object:: instanceMethod
Class::staticMethod
Class::instanceMethod
前兩種情況彙總,方法引用等價于提供方法參數的lambda表達式, 對于第三種情況,第一個參數會成為目标
類似于lambda表達式,方法引用不能獨立存在,總是會轉換為函數式接口的執行個體。
可以在方法引用中使用this參數。this::equals等同于x->this.equals(x),使用super也是合法的。
構造器引用
構造器引用于方法引用類似,隻不過方法名為new
Person::new 是person構造器的一個引用。
将一個字元串清單,可以把它轉換為一個Person對象數組。具體使用哪一個構造器呢,取決于上下文
ArrayList<String> names=.....;
Stream<Person> stream=name.stream().map(Person::new);
List<person> people=stream.collect(Collectors..toList());
如果有多個Person構造器,編譯器會選擇有一個String參數的構造器,因為它從上下文推導出這是在對一個字元串調用構造器。
可以用數組類型建立構造器引用,例如 int[]::new 是一個構造器引用,它有一個參數:即數組的長度。等價于 lambda表達式x->new int[x];
但是有一個限制,無法構造泛型類型T的數組。數組構造器引用對于客服這個限制很用。表達式 new T[n] 會産生錯誤,因為這會改為new Object[n]。對于
處理Lambda表達式:
編寫方法處理lambda表達式:
使用lambda表達式的重點是延遲執行(deferred execution)。畢竟,如果想要立即執行代碼,完全可以直接執行,則無需把它包裝在一個lambda表達式中。之是以希望以後執行:
- 在一個單獨的線程中運作代碼
- 多次運作代碼
- 在算法的恰當位置運作代碼(例如:排序中的比較操作)
- 發生某種情況時執行代碼(如,點選了一個按鈕,書記胡到達,等等)
- 隻在必要時才運作代碼
public static void repeat(int n, Runnable action){
........
}
要接受這個lambda表達式,需要選擇(偶爾可能需要提供)一個函數式接口。這裡使用Runnable接口。
常用Lambda接口:

基本類型的函數式接口:
Java核心技術實戰 再談Comparator沒有看懂,後續再看再總結吧