Java8新特性之函数式接口
一、函數接口
Lambda的設計者們為了讓現有的功能與Lambda表達式良好兼容,考慮了很多方法,于是產生了函數接口這個概念。函數接口指的是只有一個函數的接口,這樣的接口可以隱式轉換為Lambda表達式。java.lang.Runnable和java.util.concurrent.Callable是函數式接口的最佳例子。在實踐中,函數式接口非常脆弱:只要某個開發者在該接口中添加一個函數,則該接口就不再是函數式接口進而導致編譯失敗。為了克服這種代碼層面的脆弱性,并顯式說明某個接口是函數式接口,Java 8 提供了一個特殊的注解@FunctionalInterface(Java 庫中的所有相關接口都已經帶有這個注解了),舉個簡單的函數式接口的定義:
@FunctionalInterface public interface Functional {void method(); }有一點需要注意,默認方法和靜態方法不會破壞函數式接口的定義(且方法數不限),因此如下的代碼是合法的。
@FunctionalInterface public interface FunctionalDefaultMethods {void method();default void defaultMethod() {}public static void staticMethod(){} default void defaultMethod1() {}public static void staticMethod1(){} }默認方法和抽象方法之間的區別在于抽象方法需要實現,而默認方法不需要。接口提供的默認方法會被接口的實現類繼承或者覆寫
private interface Defaulable {// Interfaces now allow default methods, the implementer may or // may not implement (override) them.default String notRequired() { return "Default implementation"; } } private static class DefaultableImpl implements Defaulable { }private static class OverridableImpl implements Defaulable {@Overridepublic String notRequired() {return "Overridden implementation";} } //Defaulable接口使用關鍵字default定義了一個默認方法notRequired() 。DefaultableImpl類實現了這個接口,同時默認繼承了這個接口中的默認方法; OverridableImpl類也實現了這個接口,但覆寫了該接口的默認方法,并提供了一個不同的實現。重點注意:函數式接口就是一個有且僅有一個(除和Object中方法有相同簽名的外)抽象方法
二、lambda完整例子
三個函數式接口(無參、單參無返回、多參有返回)
@FunctionalInterface public interface MyInterface {void say(); } @FunctionalInterface public interface MyInterface1 {void SayHello(String name); } @FunctionalInterface public interface MyInterface2 {Integer calculator(Integer x,Integer y); }調用
public class realizeInterface implements MyInterface {@Overridepublic void say() {System.out.println("hello");}public static void main(String[] args) {//傳統的方法是定義一個類實現接口方法,再實例化類后進行調用realizeInterface realizeInterface=new realizeInterface();realizeInterface.say();//lambda方式實現,其中()表示show方法傳入的參數為空,只有一個參數時,(x)的括號可以省略,直接寫x/*x表示接口方法中傳入一個參數,類型會自動推斷;System.out.println(“hello from lambda”)表示實現say()方法的方法體;當方法體只有一行時,可以省略大括號*/MyInterface myInterface=()->System.out.println("hello from lambda");MyInterface1 myInterface1= x->System.out.println("The interface's arg is " + x);MyInterface2 myInterface2=(x,y)-> x+y;myInterface.say();myInterface1.SayHello("java");System.out.println("result is "+myInterface2.calculator(5, 6));} }在創建線程時的應用:
Runnable r = new Runnable() { @Override public void run() {System.out.println("Hello World!" + "This is a Thread!");}};r.run(); //基于lambda表達式實現 Runnable r = () -> System.out.println("Hello World!" + "This is a Thread!");r.run();三、java內置核心四大函數式接口
(輸入一個T類型的值,返回一個R類型的值。例如我們實現:輸入一個List 類型的值,返回該list中最大的整數值。)
(該接口實現,輸入一個T類型的值,然后返回一個boolean類型。)
綜合例子:
public class JavaFunctionalInterface {public static void testConsumer() {Consumer<Float> consumer =(x) -> {System.out.println("He consumes " + x + "¥");};consumer.accept((float) 50.5);}public static void testSupplier(){Supplier<Integer> supplier =() -> {String s = "hello world";Integer n = s.length();return n;};Integer n = supplier.get();System.out.println("n 的值為:" + n);}public static void testFunction(){Function<List<Integer>, Integer> function =(x) -> {int n = x.size();Integer value = x.get(n-1);return value;};List<Integer> list = Arrays.asList(1, 2, 3);Integer lastValue = function.apply(list);System.out.println("The lastValue is " + lastValue);}public static void testPredicate(){Predicate<String> predicate =(x) -> {int n = x.length();if (n > 10) {System.out.println("n 大于 10");return true;}else {System.out.println("n 小于 10");return false;}};predicate.test("Hello world!");}public static void main(String[] args) {testConsumer();testSupplier();testFunction();testPredicate();} }四、lambda使用之 方法引用與構造器引用
方法引用
若 Lambda 體中的功能,已經有相應的方法提供了實現,可以在Lambda體中引用該方法。(可以將方法引用理解為 Lambda 表達式的另外一種表現形式)。
舉一個簡單示例:
用該接口的Lambda表達式實現字符串的輸出功能
@Testpublic void test(){Consumer<String> consumer = (s) -> {if (s != null) {System.out.println(s);}else{System.out.println("s 為空");}};consumer.accept("java");System.out.println("------------");consumer.accept(null);} }此時,如果Lambda體中的內容已經被其它方法實現了,此時不必在Lambda體中再實現一遍了,直接調用相應的方法即可。例如有一個Info類中的show方法實現了相同的功能:
public class Info<T> {public void show(T s){if (s != null) {System.out.println(s);}else{System.out.println("s 為空");}} }此時在用Consumer的函數接口Lambda表達式時,可以用下面的形式,實現了相同的功能。
public void test(){Info<String> info = new Info<>();Consumer<String> consumer = info::show;consumer.accept("java");System.out.println("-----------");consumer.accept(null);}在上面的示例中,Consumer接口中的accept方法的參數和返回值要和Info中的show方法中的參數和返回值一致方可用上面的形式。
Lambda的方法引用形式有以下三種形式:
例如我們要用Comparator接口的compare方法實現比較兩個整數值的大小
構造器引用
public class User {private String name;private Integer age;public User() {super();}public User(String name) {super();this.name = name;}public User(String name, Integer age) {super();this.name = name;this.age = age;}@Overridepublic String toString() {return "User [name=" + name + ", age=" + age + "]";} }1、用Lambda表達式實現分別通過User的無參構造器和一個有參構造器獲得User對象
public void test5(){Supplier<User> supplier =() -> {return new User();};User user1 = supplier.get(); System.out.println("user1 : " + user1);System.out.println("-----------------------------------");Function<String, User> function =(x) -> {User user = new User(x);return user;};User user = function.apply("java");System.out.println("user : " + user);}2、Lambda的構造器引用分別實現通過User的無參構造器和一個有參構造器獲得User對象
public void test6(){Supplier<User> supplier = User::new;/*get方法無參,有返回值,所以調用的User的無參構造器,返回User對象*/User user1 = supplier.get();System.out.println("user1 : " + user1);System.out.println("-----------------------------------");Function<String, User> function = User::new;/*apply方法有一個參數,有返回值,所以調用User的一個參數的構造器,并返回User對象*/User user = function.apply("java");System.out.println("user : " + user);}兩種方式輸出結果相同:
user1 : User [name=null, age=null] ----------------------------------- user : User [name=java, age=null]Lambda的數組引用
public void test7(){Function<Integer, String[]> function =(x) -> new String[x]; //省略retuen的寫法String[] ss = function.apply(5);System.out.println("ss.length : " + ss.length);Function<Integer, String[]> function1 = String[]::new;String[] sss = function1.apply(5);System.out.println("sss.length : " + sss.length);}文章參考
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的Java8新特性之函数式接口的全部內容,希望文章能夠幫你解決所遇到的問題。