Java8 新特性,java8新特性
1.介面改善
a.介面中可以定義靜態方法
b.更重要的是,介面中的方法,可以用default修飾後,添加方法體
2.為什麼不能用預設方法來重寫equals,hashcode,toString方法?
即介面不能提供對Object類的任何方法的預設實現。如果一個類實現了一個方法,那總是優先於預設的實現的。一旦所有介面的執行個體都是Object的子類,所有介面執行個體都已經有對equals/hashCode/toString的非預設實現。因此,一個在介面上這些的預設版本都是沒用的,它也不會被編譯。
3.函數式介面
核心概念就是函數式介面。如果一個介面定義個唯一一個抽象方法,那麼這個介面就成為函數式介面。比如,java.lang.Runnable就是一個函數式介面,因為它只定義一個抽象方法:
public abstract void run();
什麼是函數式介面,有兩種情況:1.介面只有一個抽象方法,abstract修飾 2.介面只有一個抽象方法,abstract修飾。同時,包含多個預設方法,因為預設是被default修飾,不是被abstract修飾。
同時,引入了一個新的Annotation:@FunctionalInterface。可以把他它放在一個介面前,表示這個介面是一個函數式介面。加上它的介面不會被編譯,除非你設法把它變成一個函數式介面。它有點像@Override,都是聲明了一種使用意圖,避免你把它用錯。
4.Lambdas
一個函數式介面非常有價值的屬性就是他們能夠用lambdas來執行個體化。這裡有一些lambdas的例子:
左邊是指定類型的逗號分割的輸入列表,右邊是帶有return的代碼塊:
(int x, int y) -> { return x + y; }
左邊是推導類型的逗號分割的輸入列表,右邊是傳回值:
(x, y) -> x + y
左邊是推導類型的單一參數,右邊是一個傳回值:
x -> x * x
左邊沒有輸入 (官方名稱: "burger arrow"),在右邊返回一個值:
() -> x
左邊是推導類型的單一參數,右邊是沒傳回值的代碼塊(返回void):
x -> { System.out.println(x); }
靜態方法引用:
String::valueOf
非靜態方法引用:
Object::toString
繼承的函數引用:
x::toString
建構函式引用:
ArrayList::new
你可以想出一些函數引用格式作為其他lambda格式的簡寫。
| 方法引用 |
等價的lambda運算式 |
|
| String::valueOf |
|
x -> String.valueOf(x) |
| Object::toString |
|
x -> x.toString() |
| x::toString |
|
() -> x.toString() |
| ArrayList::new |
|
() -> new ArrayList<>() |
當然,在Java裡方法能被重載。類可以有多個同名但不同參數的方法。這同樣對構造方法有效。ArrayList::new能夠指向它的3個構造方法中任何一個。決定使用哪個方法是根據在使用的函數式介面。
一個lambda和給定的函數式介面在“外型”匹配的時候相容。通過“外型”,我指向輸入、輸出的類型和聲明檢查異常。
給出兩個具體有效例子:
Comparator<String> c = (a, b) -> Integer.compare(a.length(), b.length());
一個Comparator<String>的compare方法需要輸入兩個闡述,然後返回一個int。這和lambda右側的一致,因此這個任務是有效。
Runnable r = () -> { System.out.println("Running!"); }
一個Runnable的run方法不需要參數也不會傳回值。這和lambda右側一致,所以任務有效。
在抽象方法的簽名裡的受檢查異常(如果存在)也很重要。如果函數式介面在它的簽名裡聲明了異常,lambda只能拋出受檢查異常。
5.捕獲和非捕獲的Lanbdas運算式
當Lambda運算式訪問一個定義在Lambda運算式體外的非靜態變數或者對象時,這個Lambda運算式稱為“捕獲的”。比如,下面這個lambda運算式捕捉了變數x:
int x = 5; return y -> x + y;
為了保證這個lambda運算式聲明是正確的,被它捕獲的變數必須是“有效final”的。所以要麼它們需要用final修飾符號標記,要麼保證它們在賦值後不能被改變。
Lambda運算式是否是捕獲的和效能悄然相關。一個非不捕獲的lambda通常比捕獲的更高效,雖然這一點沒有書面的規範說明(據我所知),而且也不能為了程式的正確性指望它做什麼,非捕獲的lambda只需要計算一次. 然後每次使用到它都會返回一個唯一的執行個體。而捕獲的lambda運算式每次使用時都需要重新計算一次,而且從目前實現來看,它很像執行個體化一個匿名內部類的執行個體。
6.其他
lambdas不做的事
你應該記住,有一些lambdas不提供的特性。為了Java 8它們被考慮到了,但是沒有被包括進去,由於簡化以及時間限制的原因。
Non-final* 變數捕獲 - 如果一個變數被賦予新的數值,它將不能被用於lambda之中。"final"關鍵字不是必需的,但變數必須是“有效final”的(前面討論過)。這個代碼不會被編譯:
int count = 0;List<String> strings = Arrays.asList("a", "b", "c");strings.forEach(s -> { count++; // error: can't modify the value of count });
例外的透明度 - 如果一個已檢測的例外可能從lambda內部拋出,功能性的介面也必須聲明已檢測例外可以被拋出。這種例外不會散布到其包含的方法。這個代碼不會被編譯:
void appendAll(Iterable<String> values, Appendable out) throws IOException { // doesn't help with the error values.forEach(s -> { out.append(s); // error: can't throw IOException here // Consumer.accept(T) doesn't allow it });}
有繞過這個的辦法,你能定義自己的功能性介面,擴充Consumer的同時通過像RuntimeException之類拋出 IOException。我試圖用代碼寫出來,但發現它令人困惑是否值得。
控制流程程 (break, early return) -在上面的 forEach例子中,傳統的繼續方式有可能通過在lambda之內放置 "return;"來實現。但是,沒有辦法中斷迴圈或者從lambda中通過包含方法的結果返回一個數值。例如:
final String secret = "foo"; boolean containsSecret(Iterable<String> values) { values.forEach(s -> { if (secret.equals(s)) { ??? // want to end the loop and return true, but can't } });}
進一步閱讀關於這些問題的資料,看看這篇Brian Goetz寫的說明:在 Block<T>中響應“已驗證例外”super0555
其它翻譯版本(1)為什麼抽象類別不能通過利用lambda執行個體化
抽象類別,哪怕只聲明了一個抽象方法,也不能使用lambda來執行個體化。
下面有兩個類 Ordering 和 CacheLoader的例子,都帶有一個抽象方法,摘自於Guava 庫。那豈不是很高興能夠聲明它們的執行個體,像這樣使用lambda運算式?
Ordering<String> order = (a, b) -> ...;
CacheLoader<String, String> loader = (key) -> ...;
這樣做引發的最常見的爭論就是會增加閱讀lambda的難度。以這種方式執行個體化一段抽象類別將導致隱藏代碼的執行:抽象類別的構造方法。
另一個原因是,它拋出了lambda運算式可能的最佳化。在未來,它可能是這種情況,lambda運算式都不會計算到對象執行個體。放任使用者用lambda來聲明抽象類別將妨礙像這樣的最佳化。
此外,有一個簡單地解決方案。事實上,上述兩個摘自Guava 庫的執行個體類已經證明了這種方法。增加Factory 方法將lambda轉換成執行個體。
Ordering<String> order = Ordering.from((a, b) -> ...);CacheLoader<String, String> loader = CacheLoader.from((key) -> ...);
要深入閱讀,請參看由 Brian Goetz所做的說明: response to "Allow lambdas to implement abstract classes"。
java.util.function
包概要:java.util.function
作為Comparator 和Runnable早期的證明,在JDK中已經定義的介面恰巧作為函數介面而與lambdas運算式相容。同樣方式可以在你自己的代碼中定義任何函數介面或第三方庫。
但有特定形式的函數介面,且廣泛的,通用的,在之前的JD卡中並不存在。大量的介面被添加到新的java.util.function 包中。下面是其中的一些:
- Function<T, R> -T作為輸入,返回的R作為輸出
- Predicate<T> -T作為輸入,返回的boolean值作為輸出
- Consumer<T> - T作為輸入,執行某種動作但沒有傳回值
- Supplier<T> - 沒有任何輸入,返回T
- BinaryOperator<T> -兩個T作為輸入,返回一個T作為輸出,對於“reduce”操作很有用
這些最原始的特徵同樣存在。他們以int,long和double的方式提供。例如:
- IntConsumer -以int作為輸入,執行某種動作,沒有傳回值
這裡存在效能上的一些原因,主要釋在輸入或輸出的時候避免裝箱和拆箱操作。
你應該記住,有一些lambdas不提供的特性。為了Java 8它們被考慮到了,但是沒有被包括進去,由於簡化以及時間限制的原因。
Non-final* 變數捕獲 - 如果一個變數被賦予新的數值,它將不能被用於lambda之中。"final"關鍵字不是必需的,但變數必須是“有效final”的(前面討論過)。這個代碼不會被編譯:
int count = 0;List<String> strings = Arrays.asList("a", "b", "c");strings.forEach(s -> { count++; // error: can't modify the value of count });
例外的透明度 - 如果一個已檢測的例外可能從lambda內部拋出,功能性的介面也必須聲明已檢測例外可以被拋出。這種例外不會散布到其包含的方法。這個代碼不會被編譯:
void appendAll(Iterable<String> values, Appendable out) throws IOException { // doesn't help with the error values.forEach(s -> { out.append(s); // error: can't throw IOException here // Consumer.accept(T) doesn't allow it });}
有繞過這個的辦法,你能定義自己的功能性介面,擴充Consumer的同時通過像RuntimeException之類拋出 IOException。我試圖用代碼寫出來,但發現它令人困惑是否值得。
控制流程程 (break, early return) -在上面的 forEach例子中,傳統的繼續方式有可能通過在lambda之內放置 "return;"來實現。但是,沒有辦法中斷迴圈或者從lambda中通過包含方法的結果返回一個數值。例如:
final String secret = "foo"; boolean containsSecret(Iterable<String> values) { values.forEach(s -> { if (secret.equals(s)) { ??? // want to end the loop and return true, but can't } });}
進一步閱讀關於這些問題的資料,看看這篇Brian Goetz寫的說明:在 Block<T>中響應“已驗證例外”
super0555翻譯於 4年前4人頂頂 翻譯得不錯哦! 其它翻譯版本(1)為什麼抽象類別不能通過利用lambda執行個體化
抽象類別,哪怕只聲明了一個抽象方法,也不能使用lambda來執行個體化。
下面有兩個類 Ordering 和 CacheLoader的例子,都帶有一個抽象方法,摘自於Guava 庫。那豈不是很高興能夠聲明它們的執行個體,像這樣使用lambda運算式?
Ordering<String> order = (a, b) -> ...;
CacheLoader<String, String> loader = (key) -> ...;
這樣做引發的最常見的爭論就是會增加閱讀lambda的難度。以這種方式執行個體化一段抽象類別將導致隱藏代碼的執行:抽象類別的構造方法。
另一個原因是,它拋出了lambda運算式可能的最佳化。在未來,它可能是這種情況,lambda運算式都不會計算到對象執行個體。放任使用者用lambda來聲明抽象類別將妨礙像這樣的最佳化。
此外,有一個簡單地解決方案。事實上,上述兩個摘自Guava 庫的執行個體類已經證明了這種方法。增加Factory 方法將lambda轉換成執行個體。
Ordering<String> order = Ordering.from((a, b) -> ...);CacheLoader<String, String> loader = CacheLoader.from((key) -> ...);
要深入閱讀,請參看由 Brian Goetz所做的說明: response to "Allow lambdas to implement abstract classes"。
等PM翻譯於 4年前2人頂頂 翻譯得不錯哦! java.util.function
包概要:java.util.function
作為Comparator 和Runnable早期的證明,在JDK中已經定義的介面恰巧作為函數介面而與lambdas運算式相容。同樣方式可以在你自己的代碼中定義任何函數介面或第三方庫。
但有特定形式的函數介面,且廣泛的,通用的,在之前的JD卡中並不存在。大量的介面被添加到新的java.util.function 包中。下面是其中的一些:
- Function<T, R> -T作為輸入,返回的R作為輸出
- Predicate<T> -T作為輸入,返回的boolean值作為輸出
- Consumer<T> - T作為輸入,執行某種動作但沒有傳回值
- Supplier<T> - 沒有任何輸入,返回T
- BinaryOperator<T> -兩個T作為輸入,返回一個T作為輸出,對於“reduce”操作很有用
這些最原始的特徵同樣存在。他們以int,long和double的方式提供。例如:
- IntConsumer -以int作為輸入,執行某種動作,沒有傳回值
這裡存在效能上的一些原因,主要釋在輸入或輸出的時候避免裝箱和拆箱操作。
等PM翻譯於 4年前2人頂頂 翻譯得不錯哦!
本文中的所有譯文僅用於學習和交流目的,轉載請務必註明文章譯者、出處、和本文連結我們的翻譯工作遵照 CC 協議,如果我們的工作有侵犯到您的權益,請及時聯絡我們評論(85)