[Java] 17.3. 函式介面 & 預設方法

當一個介面要做為lambda的函式介面時,這時只能有一個未實作的方法,其他的必須使用default預設函式執行的內容。

 

image/svg+xml17.3. 函式介⾯與 default - function interface & default Employee e1 = new Employee( "K12345678" , "Jack" , 20 , " 男⽣ " , "048679" , 40000 ); Employee e2 = new Employee( "K00000000" , "Eric" , 25 , " 男⽣ " , "041121" , 45000 ); Employee e3 = new Employee( "K11111111" , "Mary" , 18 , " 女⽣ " , "050021" , 30000 ); Employee e4 = new Employee( "K22222222" , "Jack" , 19 , " 男⽣ " , "051212" , 42000 ); List < Employee > l = new ArrayList < Employee >(); l.add ( e1 ); l.add ( e2 ); l.add ( e3 ); l.add ( e4 ); List < Employee > result1 = Collection.filter ( l , emp -> emp.age >= 20 ); public interface Filter < T >{ boolean keep ( T item ); } import java.util.*; public class Collection { public static <T> List < T > filter ( List < T > list , Filter < T > filter ){ ArrayList < T > result = new ArrayList < T >(); for ( T item : list){ if ( filter.keep ( item ) == true ){ result.add(item) ; } } return result ; } } import java.util.*; class Main{ public static void main( String [] args){ Employee e1 = new Employee( "K12345678" , "Jack" , 20 , " 男⽣ " , "048679" , 40000 ); Employee e2 = new Employee( "K00000000" , "Eric" , 25 , " 男⽣ " , "041121" , 45000 ); Employee e3 = new Employee( "K11111111" , "Mary" , 18 , " 女⽣ " , "050021" , 30000 ); Employee e4 = new Employee( "K22222222" , "Jack" , 19 , " 男⽣ " , "051212" , 42000 ); List < Employee > l = new ArrayList < Employee >(); l.add ( e1 ); l.add ( e2 ); l.add ( e3 ); l.add ( e4 ); List < Employee > result1 = Collection.filter ( l , emp -> emp.age >= 20 ); for ( Employee emp : result1){ System.out.println(emp.age); } List < Employee > result2 = Collection.filter ( l , emp -> emp.name == "Jack" || emp.name == "Mary" ); for ( Employee emp : result2){ System.out.println(emp.name); } } } public interface Filter < T >{ boolean keep ( T item); boolean drop ( T item); } import java.util.*; class Main{ public static void main( String [] args){ Employee e1 = new Employee( "K12345678" , "Jack" , 20 , " 男⽣ " , "048679" , 40000 ); Employee e2 = new Employee( "K00000000" , "Eric" , 25 , " 男⽣ " , "041121" , 45000 ); Employee e3 = new Employee( "K11111111" , "Mary" , 18 , " 女⽣ " , "050021" , 30000 ); Employee e4 = new Employee( "K22222222" , "Jack" , 19 , " 男⽣ " , "051212" , 42000 ); List < Employee > l = new ArrayList < Employee >(); l.add(e1); l.add(e2); l.add(e3); l.add(e4); List < Employee > result1 = Collection.filter(l, emp -> emp.age >= 20 ); for ( Employee emp : result1){ System.out.println(emp.age); } } } 編譯失敗 Main.java:20: error: method filter in class Collection cannot be applied to given types; List<Employee> result1 = Collection.filter(l, emp - > emp.age >= 20); ^ required: List<T>,Filter<T> found: List<Employee>,(emp)->emp[...]>= 20 reason: cannot infer type-variable(s) T (argument mismatch; Filter is not a functional interface multiple non-overriding abstract methods found in interface Filter) where T is a type-variable: T extends Object declared in method <T>filter(List<T>,Filter<T>) 1 error import java.util.*; class Main{ public static void main( String [] args){ Employee e1 = new Employee( "K12345678" , "Jack" , 20 , " 男⽣ " , "048679" , 40000 ); Employee e2 = new Employee( "K00000000" , "Eric" , 25 , " 男⽣ " , "041121" , 45000 ); Employee e3 = new Employee( "K11111111" , "Mary" , 18 , " 女⽣ " , "050021" , 30000 ); Employee e4 = new Employee( "K22222222" , "Jack" , 19 , " 男⽣ " , "051212" , 42000 ); List < Employee > l = new ArrayList < Employee >(); l.add(e1); l.add(e2); l.add(e3); l.add(e4); final int baseAge = 20 ; List < Employee > result1 = Collection.filter(l, emp -> emp.age >= baseAge + 1 ); for ( Employee emp : result1){ System.out.println(emp.age); } int baseAge2 = 20 ; List < Employee > result2 = Collection.filter(l, emp -> { baseAge2 = baseAge2 + 1 ; return emp.age >= baseAge2; } ); for ( Employee emp : result2){ System.out.println(emp.age); } } } 編譯失敗 Main.java:36: error: local variables referenced from a lambda expression must be final or effectively final baseAge2 = baseAge2 + 1; ^ Main.java:36: error: local variables referenced from a lambda expression must be final or effectively final baseAge2 = baseAge2 + 1; ^ Main.java:37: error: local variables referenced from a lambda expression must be final or effectively final return emp.age >= baseAge2; ^ 3 errors public interface Filter < T >{ boolean keep ( T item); boolean drop ( T item); } import java.util.*; class Main{ public static void main( String [] args){ Employee e1 = new Employee( "K12345678" , "Jack" , 20 , " 男⽣ " , "048679" , 40000 ); Employee e2 = new Employee( "K00000000" , "Eric" , 25 , " 男⽣ " , "041121" , 45000 ); Employee e3 = new Employee( "K11111111" , "Mary" , 18 , " 女⽣ " , "050021" , 30000 ); Employee e4 = new Employee( "K22222222" , "Jack" , 19 , " 男⽣ " , "051212" , 42000 ); List < Employee > l = new ArrayList < Employee >(); l.add(e1); l.add(e2); l.add(e3); l.add(e4); List < Employee > result1 = Collection.filter(l, emp -> emp.age >= 20 ); for ( Employee emp : result1){ System.out.println(emp.age); } } } 編譯失敗 Main.java:20: error: method filter in class Collection cannot be applied to given types; List<Employee> result1 = Collection.filter(l, emp - > emp.age >= 20); ^ required: List<T>,Filter<T> found: List<Employee>,(emp)->emp[...]>= 20 reason: cannot infer type-variable(s) T (argument mismatch; Filter is not a functional interface multiple non-overriding abstract methods found in interface Filter) where T is a type-variable: T extends Object declared in method <T>filter(List<T>,Filter<T>) 1 error public interface Filter < T >{ boolean keep ( T item); default boolean drop ( T item) { return !keep(item) ; } } import java.util.*; class Main{ public static void main( String [] args){ Employee e1 = new Employee( "K12345678" , "Jack" , 20 , " 男⽣ " , "048679" , 40000 ); Employee e2 = new Employee( "K00000000" , "Eric" , 25 , " 男⽣ " , "041121" , 45000 ); Employee e3 = new Employee( "K11111111" , "Mary" , 18 , " 女⽣ " , "050021" , 30000 ); Employee e4 = new Employee( "K22222222" , "Jack" , 19 , " 男⽣ " , "051212" , 42000 ); List < Employee > l = new ArrayList < Employee >(); l.add(e1); l.add(e2); l.add(e3); l.add(e4); List < Employee > result1 = Collection.filter(l, emp -> emp.age >= 20 ); for ( Employee emp : result1){ System.out.println(emp.age); } } } public interface Filter < T >{ static boolean keep ( T item); } import java.util.*; class Main{ public static void main( String [] args){ Employee e1 = new Employee( "K12345678" , "Jack" , 20 , " 男⽣ " , "048679" , 40000 ); Employee e2 = new Employee( "K00000000" , "Eric" , 25 , " 男⽣ " , "041121" , 45000 ); Employee e3 = new Employee( "K11111111" , "Mary" , 18 , " 女⽣ " , "050021" , 30000 ); Employee e4 = new Employee( "K22222222" , "Jack" , 19 , " 男⽣ " , "051212" , 42000 ); List < Employee > l = new ArrayList < Employee >(); l.add(e1); l.add(e2); l.add(e3); l.add(e4); List < Employee > result1 = Collection.filter(l, emp -> emp.age >= 20 ); } } 編譯失敗 Main.java:20: error: method filter in class Collection cannot be applied to given types; List<Employee> result1 = Collection.filter(l, emp - > emp.age >= 20); ^ required: List<T>,Filter<T> found: List<Employee>,(emp)->emp[...]>= 20 reason: cannot infer type-variable(s) T (argument mismatch; Filter is not a functional interface no abstract method found in interface Filter) where T is a type-variable: T extends Object declared in method <T>filter(List<T>,Filter<T>) ./Collection.java:15: error: illegal static interface method call if(filter.keep(item) == true){ ^ the receiver expression should be replaced with the type qualifier 'Filter<T>' where T is a type-variable: T extends Object declared in method <T>filter(List<T>,Filter<T>) ./Filter.java:9: error: missing method body, or declare abstract static boolean keep(T item); ^ 4 errors J17_3_1 Filter.java 1. 定義⼀過瀘的函式介⾯ - FIlter ,其⽀援泛型,為何稱為函式 介⾯ ? 因為 Java 這⼀類會給 lambda 使⽤的介⾯稱之為函式介⾯。 1. 在此使⽤泛型的 List ,指定放入的物件為員⼯ Employee 型別的物件。 2. 若想要開發⼀個 Collection 類別,然後有個 filter() ⽅法,可以 lambda 過瀘 l 變數中的 List<Employee> 員⼯,要怎麼做呢 ? 2. 其中有個 keep() ⽅法,帶入⼀個 T 型的物件,並回傳⼀個布林值表⽰是否這個 物件要保留,過瀘器會在為 true 時保留物件、 false 時略過此物件。 函式介⾯不⼀定要有泛型。 J17_3_1 Collection.java 1. 製作⼀個集合 Collection 類別。 2. 過瀘 filter ⽅法,會將第 1 個參數 帶入的 List 透過第 2 個參數過盧函式介 Filter 物件過瀘物件。 3. 注意,這個⽅法使⽤了雙箭頭 (<>) 定義了⼀個泛型 T ,回傳型 別、參數皆使⽤了這個泛型 T 4. 依序對每個 list 中的物件進⾏ keep() 測試,若為 true 則加入⾄ result 變數中的 ArrayList 物件中。 5. 最後回傳這個過瀘後的 ArrayList 物件。 J17_3_1 Main.java 1. 在此使⽤泛型的 List ,指定放入的物件為員⼯ Employee 型別的物件。 2. 使⽤ Collection 上的 filter() ⽅法,你會發現第⼆個 參數接收的是⼀個過瀘函式介⾯ - Filter ,這時⽤ lambda 寫下的函式,會對應到 Filter 上的 boolean keep(T item) ⽅法,並實作出這個 Filter 匿名物件後帶入。 3. 在此過瀘出年齡⼤於等於 20 的員⼯。 4. 記住, lambda 的函式其實是在實作介⾯上的⽅法,實作後產⽣ 出這個函式介⾯的匿名類別的物件。在此找出 Jack Mary 5. lambda 即是應⽤即有介⾯與匿名類別的功能擴充⽽來的簡便功能。 J17_3_2 Filter.java 1. 定義⼀過瀘的函式介⾯ - Filter 2. 其中有個兩個⽅法 keep() drop() ⽅法。 J17_3_2 Main.java 3. 這時在使⽤ lambda 實作 Filter 函式介⾯時會發⽣錯誤,原因是在 Filter 函式介⾯上有兩個⽅法, lambda 不知道要實作哪個⽅法,請記 住,函式介⾯的⽅法只能有⼀個,否則無法使⽤ lambda J17_3_3 Main.java 1. 在此宣告⼀個 baseAge 變數,放入 20 作為查找員⼯的基本年 齡,特別注意的是它是 final 確定化不可變更的。 2. 如此 lambda 就可以使⽤這個 baseAge 變數,這是⼀個 closure 的技術,它可將外部的參數保留不被清除直到 lambda 使⽤完畢。 3. 即便你沒為變數加上 final ,編繹器也會將這個 使⽤在 lambda 中的 baseAge2 變數加上 final 4. 也因為如此,若試著改變外部 final 變數的值,則會發⽣編譯錯誤。 J17_3_4 Filter.java 1. 定義⼀過瀘的函式介⾯ - Filter 2. 其中有個兩個⽅法 keep() drop() ⽅法,分別表⽰保留或移除,這兩個⽅法皆有需要。 J17_3_4 Main.java 3. 但我們已知使⽤ lambda 實作 Filter 函式介⾯時會 發⽣錯誤,原因是在 Filter 函式介⾯上有兩個⽅法, lambda 不知道要實作哪個⽅法,怎麼辦好呢 ? J17_3_5 Filter.java 1. 定義⼀過瀘的函式介⾯ - FIlter 2. 其中有個兩個⽅法 keep() drop() ⽅法, 分別表⽰保留或移除,這兩個⽅法皆有需要。 3. 為了讓過瀘函式介⾯只有⼀個未實作的⽅ 法,這時可使⽤ default 關鍵字,讓 drop() ⽅罄可以在介⾯時就給定⽅法的實作。 4. default 的⽅法可以在後⾯使⽤⼤括號 ({}) 有實作的內容,在此簡單的使⽤ '!' 回傳 keep() ⽅法相⾕的布林值以表⽰需要移除。 J17_3_5 Main.java 3. 這時 lambda 即可⼀如往常的實作 Filter 函式 介⾯的 keep() ⽅法,不在會發⽣編譯錯誤。 J17_3_6 Filter.java J17_3_6 Main.java 1. 定義⼀過瀘的函式介⾯ - FIlter 2. 這個唯⼀的 keep() ⽅法是⼀個靜態化的⽅法。 3. 這時 lambda 是無法實作 keep() 的⽅法,原因是 lambda 終是會建立⼀個匿名類別的物件,⽽靜態化的⽅法是不可被實作 的,因此不可收靜態化的函式作為 lambda 實作的⽅法。 當然,⼀個函式介⾯除了 lambda 要作的唯⼀⽅法外,還有其他靜態化 static 的函式,這也是沒問題的,因為靜態化的⽅法並不是 lambda 考慮實 作的對像。 想要使⽤ lambda 快速篩選員⼯,能做到嗎 ? 1. ⾃訂 Filter 函式介⾯ 2. 製作 Collection 類別與 fi lter ⽅法 3. 使⽤ lambda 實現 Filter 函式介⾯ 1 1 1 1 lambda closure ,引⽤的外部參數必須是 fi nal 注意,函式介⾯只能有⼀個抽象⽅法 Filter 函式介⾯真的需要有兩個⽅法 keep() drop() 怎麼辦好呢 ? 1 1 1.default ,指定其他⽅法預設⽅法 2. lambda 實作未實作的 Keep() ⽅法 注意, lambda 對介⾯上的 ' 靜態⽅法 ' 無效 1 1

留言