本文作者:楊龍百炬,叩丁狼高級講師蚕脏。原創(chuàng)文章侦副,轉(zhuǎn)載請注明出處。
基類型和包裝類型
拆箱裝箱的性能問題
將基本類型轉(zhuǎn)換為裝箱類型驼鞭,稱為裝箱秦驯,反之則稱為拆箱,兩者都需要額外的計算開銷挣棕。包裝類型在求和的時候译隘,會出現(xiàn)性能問題。
所以我們會把下面 count 類型聲明基本類型 long洛心。
Long count = 0;
for(int i = 1; i <= 10; i++){
count += i; // 這會先拆箱后又裝箱
}
System.out.println(count);
函數(shù)編程中如何解決
那么在函數(shù)變成里面若求和是不是會出現(xiàn)上述問題呢固耘?在 Java 8 中,僅對整型词身、長整型和雙浮點型做了特殊處理厅目,因為它們在數(shù)值計算中用得最多,特殊處理后的系統(tǒng)性能提升效果最明顯偿枕。提供下面類型下面這樣函數(shù):
接口 | 參數(shù) | 返回類型 |
---|---|---|
ToLongFunction | T | long |
LongFunction | long | T |
LongUnaryOperator | long | long |
ToIntFunction | T | int |
IntFunction | int | T |
IntUnaryOperator | int | int |
ToDoubleFunction | T | double |
DoubleFunction | double | T |
DoubleUnaryOperator | double | double |
Stream 中 map 和 flatMap 的方法的 Lambda 表達式須是以上接口的一個實例璧瞬,這些基本類型都有與之對應(yīng)的 Stream户辫,以基本類型名為前綴渐夸,如 LongStream 。事實上 mapToLong 方法返回的不是一個一般的 Stream渔欢,而是一個特殊處理的 Stream墓塌。這些特殊的 Stream 還提供額外的方法,避免重復(fù)實現(xiàn)一些通用的方法奥额,讓代碼更能體現(xiàn)出數(shù)值計算的意圖苫幢。包含一個 summaryStatistics 方法,這個方法能計算出各種各樣的統(tǒng)計值垫挨,如 IntStream 對象內(nèi)所有元素中的最小值韩肝、最大值、平均值以及數(shù)值總和九榔。
IntStream intStream = Stream.of(1, 2, 3, 4).mapToInt(value -> value);
IntSummaryStatistics intSummaryStatistics = intStream.summaryStatistics();
System.out.println(intSummaryStatistics.getAverage());
System.out.println(intSummaryStatistics.getCount());
System.out.println(intSummaryStatistics.getMax());
System.out.println(intSummaryStatistics.getMin());
/* System.out.println(intStream.average()); 上面那些方法等價下面這些方法
System.out.println(intStream.count());
System.out.println(intStream.max());
System.out.println(intStream.min()); */
重載解析問題
Java 方法中是可以存在重載的哀峻,即同類中方法名相同涡相,方法簽名不相同,但若參數(shù)是一個 Lambda 表達式剩蟀,出現(xiàn)調(diào)用歧義催蝗。
interface IntPredicate {
public boolean test(int value);
}
class MethodOverloadTest {
public static void overloadedMethod(Predicate<Integer> predicate) {
System.out.print("Predicate");
}
public static void overloadedMethod(IntPredicate predicate) {
System.out.print("IntPredicate");
}
}
public class Main {
public static void main(String[] args) {
MethodOverloadTest.overloadedMethod((x) -> true); // 這里編譯會報錯,調(diào)用方法出現(xiàn)歧義
}
}
最好的解決辦法是不該再重載育特,而應(yīng)當開始重新命名重載方法丙号。
@FunctionalInterface
每個用作函數(shù)接口的接口最好添加這個注解。
該注解會強制 javac 檢查一個接口是否符合函數(shù)接口的標準缰冤。如果該注解添加給一個枚舉類型犬缨、類或另一個注解,或者接口包含不止一個抽象方法棉浸, javac 就會報錯遍尺。重構(gòu)代碼時,使用它能很容易發(fā)現(xiàn)問題涮拗。
接口中默認方法
兼容性問題
雖然 Java 在持續(xù)演進乾戏,但它一直在保持著向后二進制兼容。具體來說三热,使用 Java 1 到 Java 7 編譯的類庫或應(yīng)用鼓择,可以直接在 Java 8 上運行。
Java 8 中為 Collection 接口增加了 stream 方法就漾,這意味著所有實現(xiàn)了 Collection 接口的類都必須增加這個新方法呐能。對核心類庫里的類來說,實現(xiàn)這個新方法抑堡。但在 JDK 之外實現(xiàn) Collection 接口的類摆出,例如 MyCustomList,也仍然需要實現(xiàn)新增的 stream 方法首妖。這就帶來了不兼容的問題偎漫。
解決兼容性問題
在 Java8 中,Collection 接口告訴它所有的子類:“如果你沒有實現(xiàn) stream 方法有缆,就使用我的吧”象踊,接口中這樣的方法叫作默認方法(使用 default 關(guān)鍵字修飾),那么實現(xiàn)類不實現(xiàn)該方法棚壁,也不會編譯報錯杯矩。
Collection {
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
}
默認方法的重寫規(guī)則
- 要優(yōu)先選擇子類中定義方法。
- 若子類沒有此方法袖外,父類類中重寫的方法優(yōu)先級高于父接口中定義的方法史隆。
interface IWalkable {
default void walk(){
System.out.println("IWalkable walk method");
}
}
class Dog implements IWalkable {
public void walk(){
System.out.println("Dog walk method");
}
}
interface IDanceWalk extends IWalkable{
default void walk(){
System.out.println("IDanceWalk walk method");
}
}
class Husky extends Dog implements IDanceWalk {
}
public class Main {
public static void main(String[] args) {
IWalkable walkable = new Dog(); // Dog walk method, 證明第一條重寫規(guī)則
walkable.walk();
IWalkable walkable2 = new Husky(); // Dog walk method, 證明第二條重寫規(guī)則
walkable2.walk();
}
}
接口多實現(xiàn)繼承問題
當實現(xiàn)類實現(xiàn)多個接口時,多個接口中同時存在相同方法簽名的方法曼验,實現(xiàn)類并沒有覆蓋泌射。
interface A {
public default String myMehtod() {
return "A";
}
}
interface B {
public default String myMehtod() {
return "B";
}
}
// javac 并不明確應(yīng)該繼承哪個接口中的方法头镊,
// 因此編譯器會報錯 C inherits unrelated defaults for myMehtod() from types A and B
class C implements A, B {
}
解決辦法,實現(xiàn)類中覆蓋接口中方法:
class C implements A, B {
public String myMehtod() {
return A.super.myMehtod();
}
}
接口中靜態(tài)方法
Stream 是個接口魄幕,Stream.of 是接口的靜態(tài)方法相艇。這也是 Java 8 中添加的一個新的語言特性,旨在幫助編寫類庫的開發(fā)人員纯陨,但對于日常應(yīng)用程序的開發(fā)人員也同樣適用坛芽。
比如我們自己開發(fā)一個工具類的時候,里面會定義很多靜態(tài)方法翼抠,而這些方法屬性類咙轩,不屬于對象,為了不讓使用者創(chuàng)建該工具類的對象阴颖,一般會把類聲明成私有或者把其構(gòu)造器私有活喊,稍微麻煩一些。
Java8 支持接口中添加靜態(tài)方法量愧,那么以后工具都可以使用接口來定義了更加方便钾菊。
Optional
Optional 是為核心類庫新設(shè)計的一個數(shù)據(jù)類型,用來替換 null 值偎肃。人們常常使用 null 值表示值不存在煞烫,Optional 對象能更好地表達這個概念。Optional 對象相當于值的容器累颂。
使用 null 代表值不存在的最大問題在于 NullPointerException滞详。一旦引用一個存儲 null 值的變量,程序會立即崩潰紊馏。使用 Optional 對象有兩個目的:首先料饥,Optional 對象鼓勵程序員適時檢查變量是否為空,以避免代碼缺陷朱监;其次岸啡,它將一個類的 API 中可能為空的值文檔化,這比閱讀實現(xiàn)代碼要簡單很多赌朋。
Optional API
- static <T> Optional<T> of(T value) 凰狞,可以從某個值創(chuàng)建出一個 Optional 對象篇裁。
- static <T> Optional<T> ofNullable(T value)沛慢,可以從某個值(包含 null)創(chuàng)建出一個 Optional 對象。
- static <T> Optional<T> empty()达布,返回一個沒有值的 Optional 對象团甲。
- boolean isPresent(),判斷 Optional 對象中是否有值黍聂。
- T get()躺苦,從 Optional 對象獲取值身腻。
- T orElse(T other),從 Optional 對象獲取值匹厘,當 Optional 對象為空時嘀趟,該方法提供了一個備選值。
- T orElseGet(Supplier<? extends T> other)愈诚,從 Optional 對象獲取值她按,當 Optional 對象為空時,該方法提供了一個備選值炕柔,適合備選值計算復(fù)雜時使用酌泰。
- <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier),從 Optional 對象獲取值匕累,當 Optional 對象為空時陵刹,拋出異常,不設(shè)置 exceptionSupplier 的值拋出的異常是空指針欢嘿,也可以通過 exceptionSupplier 來自定以拋出異常衰琐。
Optional<String> a = Optional.of("a");
assertEquals("a", a.get());
assertTrue(a.isPresent());
Optional emptyOptional = Optional.empty();
Optional alsoEmpty = Optional.ofNullable(null);
assertFalse(emptyOptional.isPresent());
assertFalse(alsoEmpty.isPresent());
assertEquals("b", emptyOptional.orElse("b"));
assertEquals("c", emptyOptional.orElseGet(() -> "c"));
emptyOptional.orElseThrow(null); // 拋出空指針異常
emptyOptional.orElseThrow(() -> new RuntimeException("無值")); // 拋出運行時異常
想獲取更多技術(shù)干貨,請前往叩丁狼官網(wǎng):http://www.wolfcode.cn/all_article.html