函數式(Functional)介面 只包含一個抽象方法的介面,稱為函數式介面。 你可以通過Lambda表達式來創建該介面的對象。(若Lambda表達式拋出一個受檢異常(即:非運行時異常),那麼該異常需要在目標介面的抽象方法上進行聲明 我們可以在一個介面上使用@Functionallnterface註 ...
函數式(Functional)介面
- 只包含一個抽象方法的介面,稱為函數式介面。
- 你可以通過Lambda表達式來創建該介面的對象。(若Lambda表達式拋出一個受檢異常(即:非運行時異常),那麼該異常需要在目標介面的抽象方法上進行聲明
- 我們可以在一個介面上使用
@Functionallnterface
註解,這樣做可以檢查它是否是一個函數式介面。同時javadoc也會包含一條聲明,說明這個介面是一個函數式介面。 - 在java.util.function包下定義了Java8的豐富的函數式介面
如何理解函數式介面
- Java從誕生日起就是一直倡導“一切皆對象”,在Java裡面面向對象(OOP)編程是一切。但是隨著python、scala等語言的興起和新技術的挑戰,Java不得不做出調整以便支持更加廣泛的技術要求,也即java不但可以支持OOP還可以支持OOF(面向函數編程)
- 在函數式編程語言當中,函數被當做一等公民對待。在將函數作為一等公民的編程語言中,Lambda表達式的類型是函數。
- 但是在Java8中,Lambda表達式是對象,而不是函數,它們必須依附於一類特別的對象類型一函數式介面。
- 簡單的說,在Java8中,Lambda表達式就是一個函數式介面的實例。這就是Lambda表達式和函數式介面的關係。也就是說,只要一個對象是函數式介面的實例,那麼該對象就可以用Lambda表達式來表示。
- 所以以前用匿名實現類表示的現在都可以用Lambda表達式來寫。
Java內置四大核心函數式介面以及使用
// Consumer
public void happyTime(double money, Consumer<Double> con){
con.accept(money);
}
@Test
public void test1(){
happyTime(200, money -> {
System.out.println("happy: " + money);
});
}
// Predicate
public List<String> filterString(List<String> list, Predicate<String> pred){
List<String> res = new ArrayList<>();
for(String s : list) {
if(pred.test(s)){
res.add(s);
}
}
return res;
}
@Test
public void test2(){
List<String> list = Arrays.asList("北京","南京","西京","廣東","東京");
List<String> res = filterString(list, str -> !str.contains("京"));
System.out.println(res);
}
方法引用
當要傳遞給Lambda體的操作,已經有實現的方法了,可以使用方法引用!
方法引用可以看做是Lambda表達式深層次的表達。換句話說,方法引用就是Lambda表達式,也就是函數式介面的一個實例,通過方法的名字來指向一個方法,可以認為是Lambda表達式的一個語法糖。
要求:實現介面的抽象方法的參數列表和返回值類型,必須與方法引用的方法的參數列表和返回值類型保持一致!(針對情況1和2)
格式:使用操作符“::
”將類(或對象)與方法名分隔開來。
如下三種主要使用情況:
- 對象 :: 實例方法名(非靜態方法)
- 類 :: 靜態方法名
- 類 :: 實例方法名(非靜態方法)
對象調用靜態方法實際上相當於類調用靜態方法.
使用情景
當要傳遞給Lambda體的操作,已經有實現的方法了,可以使用方法引用!
// 對象 :: 實例方法
@Test
public void test1(){
// Consumer中的void accept(T t)
// PrintStream中的void println(T t)
// 兩種都是泛型T, 可以用
Consumer<String> con1 = str -> System.out.println("str");
Consumer<String> con2 = System.out::println;
PrintStream out = System.out;
Consumer<String> con3 = out::println;
con3.accept("con3");
}
@Test
public void test2(){
// Supplier中的T get()
// Employee中的String getName()
// 可以做類型推斷
Employee emp = new Employee(1001, "tom", 23, 5600);
Supplier<String> sup1 = () -> emp.getName();
Supplier<String> sup2 = emp::getName;
System.out.println(sup2.get());
}
// 類 :: 靜態方法
@Test
public void test3(){
// Comparator中的int compare(T t1, T t2)
// Integer中的int compare(T t1, T t2)
Comparator<Integer> com1 = (t1, t2) -> Integer.compare(t1, t2);
Comparator<Integer> com2 = Integer::compare;
System.out.println(com2.compare(3, 4));
}
@Test
public void test4(){
// Function<T, R>
// Function中的R apply(T t),
// Math中的Long round(Double d)
// 註意參數類型要對上
Function<Double, Long> f = new Function<Double, Long>() {
@Override
public Long apply(Double aDouble) {
return null;
}
};
Function<Double, Long> f2 = Math::round;
Function<Double, Long> f3 = d -> Math.round(d);
System.out.println(f2.apply(4.0));
}
// 類 :: 實例方法名(非靜態方法)
// 註意調用者是誰, 比如t1.comparaTo(t2)
@Test
public void test5(){
// Comparator中的 int compare(T t1, T t2)
// String中的 int t1.compareTo(t2)
Comparator<String> com1 = (s1, s2) -> s1.compareTo(s2);
Comparator<String> com2 = String :: compareTo;
System.out.println(com2.compare("a", "e"));
}
@Test
public void test6(){
//BiPredicate中的boolean test(T t1, T t2);
//String中的boolean t1.equals(t2)
BiPredicate<String, String> bp1 = (t1, t2) -> t1.equals(t2);
BiPredicate<String, String> bp2 = String :: equals;
System.out.println(bp2.test("a", "a"));
}
@Test
public void test7(){
// Function中的 R apply(T t)
// Employee中的String getName()
Function<Employee, String> f = Employee::getName;
Employee emp = new Employee(1234,"tmo", 12, 2314.23);
System.out.println(f.apply(emp));
}
構造器引用
和方法引用類似,函數式介面的抽象方法的形參列表 和 構造器的形參列表一致。抽象方法的返回值類型即為構造器所屬的類的類型.
@Test
public void test1(){
// Supplier的T get()
// Employee的空參構造器, 返回Employee對象
Supplier<Employee> sup1 = Employee :: new;
System.out.println(sup1.get());
}
@Test
public void test2(){
// Function中的 R apply(T t)
Function<Integer, Employee> f = Employee :: new;
System.out.println(f.apply(1234));
}
@Test
public void test3(){
// BiFunction中的 R apply(T t, U u)
BiFunction<Integer, String, Employee> bf = (id, name) -> new Employee(id, name);
BiFunction<Integer, String, Employee> bf1 = Employee::new;
System.out.println(bf1.apply(1234, "joey"));
}
數組引用
把數組看成一種類的類型, 就跟構造器引用沒有太大區別了.
@Test
public void test4(){
Function<Integer, String[]> f = length -> new String[length];
Function<Integer, String[]> f1 = String[] :: new;
System.out.println(f1.apply(5).length);
}
附錄
public class Employee {
private int id;
private String name;
private int age;
private double salary;
// ... getter, setter, toString, constructor
}