JDK1.8新特性——接口改動和Lambda表達式


JDK1.8新特性——接口改動和Lambda表達式

摘要:本文主要學習了JDK1.8的新特性中有關接口和Lambda表達式的部分。

部分內容來自以下博客:

https://www.cnblogs.com/onetwo/p/8526374.html

https://www.cnblogs.com/xxez-d/p/5989944.html

https://www.cnblogs.com/runningTurtle/p/7092632.html

https://www.cnblogs.com/jiangwz/p/9197238.html

https://www.cnblogs.com/hujingnb/p/10181630.html

接口增強

在JDK1.8以前的版本中,定義一個接口時,所有的方法必須是抽象方法,不能有具體實現,這是Java語法規定的。但是在JDK1.8中定義一個接口時,在滿足特定的前提下,可以有方法的具體實現。這樣一個接口中可以有屬性,可以有抽象方法,也可以有具體的方法,這跟JDK1.8以前的接口比,明顯接口的功能變得強大了。

使用接口增強

接口中定義具體的方法實現是有限制的,它不能像我們在一個普通類那樣隨便定義方法實現,它只能定義default和static類型的方法。

在調用的時候,被default修飾的默認方法需要通過實現了該接口的類去調用,被static修飾的靜態方法可以通過接口名直接調用。

default和static不能用來修飾Object中的public方法,但是可以修飾Object中其他訪問修飾符修飾的方法。

代碼如下:

 1 public class Demo {
 2     public static void main(String[] args) {
 3         NewInterface.testStatic();
 4         new NewInterfaceImpl().testDefault();
 5     }
 6 }
 7 
 8 interface NewInterface {
 9     public String name = "";
10     
11     default void testDefault() {
12         System.out.println("測試新增的default方法");
13     }
14     
15     static void testStatic() {
16         System.out.println("測試新增的static方法");
17     }
18 }
19 
20 class NewInterfaceImpl implements NewInterface {
21     @Override
22     public void testDefault() {
23         NewInterface.super.testDefault();
24         System.out.println("測試子類重寫的新增的static方法");
25     }
26 }

運行結果如下:

1 測試新增的static方法
2 測試新增的default方法
3 測試子類重寫的新增的static方法

好處

想象這樣一中情況,當多個類實現一個接口的某個方法時,方法的具體實現代碼相同,這樣就會造成代碼重復問題。接口增強就相當於把公共的代碼抽離出來,放入接口定義中,這樣實現類對於該方法就不用重新定義,直接調用即可,這很好的解決了實現該接口的子類代碼重復的問題。

函數式接口

所謂的函數式接口,當然首先是一個接口,然后就是在這個接口里面只能有一個抽象方法,這種類型的接口也稱為SAM(Single Abstract Method)接口。

下面的代碼展示了一個簡單的函數式接口:

1 interface NewInterface {
2     void test();
3 }

關於@FunctionalInterface注解

JDK1.8為函數式接口引入了一個新注解@FunctionalInterface,主要用於編譯級錯誤檢查,加上該注解,當你寫的接口不符合函數式接口定義的時候,編譯器會報錯。

加不加@FunctionalInterface對於接口是不是函數式接口沒有影響,該注解只是提醒編譯器去檢查該接口是否僅包含一個抽象方法。

下面的代碼展示了一個加了注解的函數式接口:

1 @FunctionalInterface
2 interface NewInterface {
3     void test();
4 }

函數式接口里允許定義默認方法

函數式接口里是可以包含默認方法,因為默認方法不是抽象方法,其有一個默認實現,所以是符合函數式接口的定義的。

下面的代碼展示了一個帶有默認方法的函數式接口:

 1 @FunctionalInterface
 2 interface NewInterface {
 3     void test();
 4     
 5     default void defaultRead() {
 6         System.out.println("defaultRead() ...");
 7     }
 8     
 9     default void defaultWrite() {
10         System.out.println("defaultWrite() ...");
11     }
12 }

函數式接口里允許定義靜態方法

函數式接口里是可以包含靜態方法,因為靜態方法不能是抽象方法,是一個已經實現了的方法,所以是符合函數式接口的定義的。

下面的代碼展示了一個帶有靜態方法的函數式接口:

 1 @FunctionalInterface
 2 interface NewInterface {
 3     void test();
 4     
 5     static void staticRead() {
 6         System.out.println("staticRead() ...");
 7     }
 8     
 9     static void staticWrite() {
10         System.out.println("staticWrite() ...");
11     }
12 }

函數式接口里允許定義java.lang.Object里的public方法

函數式接口里是可以包含Object里的public方法,這些方法對於函數式接口來說,不被當成是抽象方法(雖然它們是抽象方法)。因為此接口的實現類一定是Object的子類,會自動實現這個接口中定義的抽象方法。

但也只能是public方法,其他訪問修飾符修飾的方法是不允許在函數式接口里定義的。

下面的代碼展示了一個帶有Object類的public方法的函數式接口:

 1 @FunctionalInterface
 2 interface NewInterface {
 3     void test();
 4     
 5     @Override
 6     public String toString();
 7     
 8     @Override
 9     public boolean equals(Object obj);
10     
11 //    @Override
12 //    public Object clone();
13 }

JDK1.8新增的函數式接口

JDK1.8內置的函數式接口放在包java.util.function下,默認在JDK安裝路徑下的src.zip中。

Supplier接口

Supplier接口沒有參數,只有返回值,作用是為了產生對象。

1 @FunctionalInterface
2 public interface Supplier<T> {
3     T get();
4 }

Consumer接口

Consumer接口有參數,但是沒有返回值,作用是為了進行某種操作,比如打印操作。

1 @FunctionalInterface
2 public interface Consumer<T> {
3     void accept(T t);
4 
5     default Consumer<T> andThen(Consumer<? super T> after) {
6         Objects.requireNonNull(after);
7         return (T t) -> { accept(t); after.accept(t); };
8     }
9 }

Function接口

Function接口有參數,也有返回值,作用是對參數進行處理並返回。

 1 @FunctionalInterface
 2 public interface Function<T, R> {
 3     R apply(T t);
 4 
 5     default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
 6         Objects.requireNonNull(before);
 7         return (V v) -> apply(before.apply(v));
 8     }
 9 
10     default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
11         Objects.requireNonNull(after);
12         return (T t) -> after.apply(apply(t));
13     }
14 
15     static <T> Function<T, T> identity() {
16         return t -> t;
17     }
18 }

Predicate接口

Predicate接口有參數,也有返回值,但返回值是Boolean類型的值,作用是對傳入的參數進行某種判斷。

 1 @FunctionalInterface
 2 public interface Predicate<T> {
 3     boolean test(T t);
 4 
 5     default Predicate<T> and(Predicate<? super T> other) {
 6         Objects.requireNonNull(other);
 7         return (t) -> test(t) && other.test(t);
 8     }
 9 
10     default Predicate<T> negate() {
11         return (t) -> !test(t);
12     }
13 
14     default Predicate<T> or(Predicate<? super T> other) {
15         Objects.requireNonNull(other);
16         return (t) -> test(t) || other.test(t);
17     }
18 
19     static <T> Predicate<T> isEqual(Object targetRef) {
20         return (null == targetRef)
21                 ? Objects::isNull
22                 : object -> targetRef.equals(object);
23     }
24 }

Lambda表達式

什么是Lambda表達式

通過使用表達式來提供一種清晰簡潔的方式來表示方法接口。

語法

參數列表 -> 方法體

如果參數列表有多個值,可以使用小括號包含,如果只有一條,則可以省略小括號。

如果方法體有多條語句,可以使用大括號包含,如果只有一條,則可以省略大括號,如果是return語句,則可以省略return關鍵字。

使用舉例

在創建一個線程並調用的時候,需要傳入一個Runnable接口的實現類。

使用Lambda表達式之前:

1 new Thread(new Runnable() {
2     @Override
3     public void run() {
4         System.out.println("Thread1 run() ...");
5     }
6 }).start();

使用Lambda表達式之后:

1 new Thread(() -> System.out.println("Thread2 run() ...")).start();

變量作用域

Lambda表達式只能引用標記了final的外層局部變量,這就是說不能在Lambda內部修改定義在域外的局部變量,否則會編譯錯誤。

1 final int num = 100;
2 NewInterface ni = i -> String.valueOf(i + num);
3 System.out.println(ni.test(num));

Lambda表達式的局部變量可以不用聲明為final,但是必須不可被后面的代碼修改(即隱性的具有final的語義)。

1 final int num = 100;
2 NewInterface ni = i -> String.valueOf(i + num);
3 System.out.println(ni.test(num));
4 // num++;

在Lambda表達式當中不允許聲明一個與局部變量同名的參數或者局部變量。

1 final int num = 100;
2 // NewInterface ni = num -> String.valueOf(num);
3 NewInterface ni = i -> String.valueOf(i);
4 System.out.println(ni.test(num));

方法引用

什么是方法引用

方法引用是JDK1.8中提出的用來簡化Lambda表達式的一種手段。它通過類名和方法名來定位到一個靜態方法或者實例方法。

語法

類或對象名::方法名或new

靜態方法引用

如果是靜態方法引用,那么語法是 類名::靜態方法名 ,並且方法調用傳入的參數就是方法用到的參數。

代碼如下:

 1 public class Demo {
 2     public static void main(String[] args) {
 3         // 使用匿名內部類
 4 //        NewInterface ni = new NewInterface() {
 5 //            @Override
 6 //            public String test(String name) {
 7 //                return String.valueOf(name);
 8 //            }
 9 //        };
10         // 使用Lambda表達式
11 //        NewInterface ni = name -> String.valueOf(name);
12         // 使用方法引用
13         NewInterface ni = String::valueOf;
14         System.out.println(ni.test("name"));
15     }
16 }
17 
18 @FunctionalInterface
19 interface NewInterface {
20     String test(String name);
21 }

運行結果如下:

1 name

實例方法引用

如果是實例方法引用,那么語法是 對象實例::方法名 ,並且方法調用傳入的第一個參數是方法的調用者,后面的參數才是方法的參數。

代碼如下:

 1 public class Demo {
 2     public static void main(String[] args) {
 3         // 使用匿名內部類
 4 //        NewInterface ni = new NewInterface() {
 5 //            @Override
 6 //            public int test(String str) {
 7 //                return str.length();
 8 //            }
 9 //        };
10         // 使用Lambda表達式
11 //        NewInterface ni = str -> str.length();
12         // 使用方法引用
13         NewInterface ni = String::length;
14         System.out.println(ni.test("lenth"));
15     }
16 }
17 
18 @FunctionalInterface
19 interface NewInterface {
20     int test(String str);
21 }

運行結果如下:

1 5

構造方法引用

如果是構造方法引用,那么語法是 類名::new ,並且方法調用傳入的參數就是構造方法的參數。

代碼如下:

 1 public class Demo {
 2     public static void main(String[] args) {
 3         // 使用匿名內部類
 4 //        NewInterface ni = new NewInterface() {
 5 //            @Override
 6 //            public String test(String str) {
 7 //                return new String(str);
 8 //            }
 9 //        };
10         // 使用Lambda表達式
11 //        NewInterface ni = str -> new String(str);
12         // 使用方法引用
13         NewInterface ni = String::new;
14         System.out.println(ni.test("str"));
15     }
16 }
17 
18 @FunctionalInterface
19 interface NewInterface {
20     String test(String str);
21 }

運行結果如下:

1 str


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM