Java8用Optional 讓“空”中的攻城獅接地氣——走進Java Lambda(五)

    Optional是Java8裏面用避免空指針的,無論什麼語言“空指針異常”總會是最困擾人的東西,老實說這很有可能就是邏輯錯誤。爲了避免這樣的錯誤,Java8建議使用Optional來培養[避免空指針]的好習慣。Optional的方法大綱如下(冒號後表示返回值):

empty() : Optional<T>

of(T) : Optional<T>

ofNullable(T) : Optional<T>

get() : T

isPresent() : boolean

ifPresent(Consumer<? super T>) : void

filter(Predicate<? super T>) : Optional<T>

map(Function<? super T, ? extends U>) : Optional<U>

flatMap(Function<? super T, Optional<U>>) : Optional<U>

orElse(T) : T

orElseGet(Supplier<? extends T>) : T

orElseThrow(Supplier<? extends X>) : T

 

在之前的篇幅中有個求累加的例子是這麼寫的。

@Test
public void reduceWithInt() {
    int sumAll = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);// 給一個0是用來啓動,的,若給-1,結果會是9</span>
    System.out.println(sumAll);// 10
}

例子中調用reduce方法時,我們給了一個初始值0.若我們不給初始值呢會出現什麼情況?不給初始值,我們的代碼該這樣寫:

@Test
public void reduceTest() {
    Optional<Integer> sumAll = Stream.of(1, 2, 3, 4).reduce(Integer::sum);// 注意返回值類型
    System.out.println(sumAll);// Optional[10]     注意輸出值不再是10了
}

那麼這個Optional是個什麼呢?Optional<T>是Java用來解決空指針異常的一個東西。不過呢,我們想要得到的結果是10,但返回的是Optional[10],怎麼把10弄出來?看下面代碼

@Test
public void reduceTest() {
    Integer testInt[]={1, 2, 3, 4};
    Optional<Integer> sumAll = Stream.of(testInt).reduce(Integer::sum);
    System.out.println(sumAll.get());// 10
}

調用一次Optionalget()方法,就得到真實結果了。猜想一下下面代碼會是什麼?

@Test
public void reduceTest() {
    Integer testInt[]={};//空數組
    Optional<Integer> sumAll = Stream.of(testInt).reduce(Integer::sum);
    System.out.println(sumAll.get());
}

居然保錯了,親!

java.util.NoSuchElementException: No value present

at java.util.Optional.get(Optional.java:135)

看到了麼,所謂避免“空指針異常”就是用“元素缺失異常”來替換?當然不是啦。這裏對空數組沒有做任何操作,沒有初始值,當然是“null”了,爲了不拋異常。只有在確定不爲空的時候才調用get()方法了,怎麼知道爲不爲空呢,那就是調用isPresent()方法判斷是否“元素存在”,存在就可以調用。比如下面代碼:

@Test
public void reduceTest() {
    Integer testInt[]={};
    Optional<Integer> sumAll = Stream.of(testInt).reduce(Integer::sum);
    System.out.println(sumAll);// Optional.empty
    if(sumAll.isPresent()){
        System.out.println(sumAll.get());
    }else{
        System.out.println(sumAll.empty());// Optional.empty
    }
}

從這段代碼,我們看到了,當Optional裏面值缺失時,直接打印Optional和調用empty()方法,得到的結果是一樣的,即打印“Optional.empty”字符串。你要是覺得多一個判斷好麻煩,那麼沒問題,你可以用ifPresent來代替,在ifPresent裏面加入業務邏輯,只有不爲空的時候才處理。上面的代碼可以改成如下:

@Test
public void reduceTest() {
    Integer testInt[] = {};
    Optional<Integer> sumAll = Stream.of(testInt).reduce(Integer::sum);
    System.out.println(sumAll);
    sumAll.ifPresent(x -> {
        System.out.println(x); //sumAll不爲空的時候,打印x的值;爲空的時候,不做任何操作
    });
}

Java8這麼搞,究竟幾個意思。其實呢,人家的意思就,今後忘掉“空”(null)吧。要養成良好的習慣,不要出現“空”。但是爲了兼容“空”狀態所以用了一個“缺省”。在上面例子中我們可以給Optional一個默認值0,即如下寫法。

@Test
public void reduceTest() {
    Integer testInt[]={};
    Optional<Integer> sumAll = Stream.of(testInt).reduce(Integer::sum);
    System.out.println(sumAll.orElse(0));// 0
}

這樣程序就不會報錯了,值缺省(null)時會返回默認值,但是這樣寫有個問題,那就是默認值是先new出來的,佔有一定空間呀,有沒有一種方式,缺省時再去new呢。有那就是orElseGet(Supplier<? extends T>),這段代碼就可以改成下面這樣:

@Test
public void reduceTest() {
    Integer testInt[]={};
    Optional<Integer> sumAll = Stream.of(testInt).reduce(Integer::sum);
    System.out.println(sumAll.orElseGet(() -> 0)); // 0
}

雖然看到的結果是一樣,但是這種寫法更省內存空間了。

  可能有這種情況,就是,值出現缺省狀態,在業務上就是錯了,這個時候你很想拋個特定的異常出來,怎麼弄呢。你只需要調用orElseThrow方法就可以了,比如用.orElseThrow(() -> new Throwable("不能爲空"))

   以上所有例子中Optional都是方法返回給我們的,其實我們也可以自己new 一個Optional但是不是使用new關鍵字。而是使用 of(T)以及ofNullable(T) 等方法。但是ofofNullable是有區別的。我們看看下面這段代碼:

@Test
public void reduceTest() {
    Optional<List<Integer>> optional = Optional.of(new ArrayList<>());//of()裏面不能傳入null
    System.out.println(optional.get()); // []
    Optional<List<Integer>> optional1 = Optional.ofNullable(new ArrayList<>());
    System.out.println(optional1.get()); // []
    Optional<List<Integer>> emptyOptional = Optional.empty();
    Optional<List<Integer>> emptyOptional2 = Optional.ofNullable(null);
    System.out.println(emptyOptional.equals(emptyOptional2)); //true
}

Of裏面必須輸入一個值,也就是說使用of創建的Optional對象一定不是empty的,而ofNullable是可以創建一個emptyOptional對象。

Optional除了這麼蒼白地使用,還可配合過濾轉換一起使用,比如我們要對{1,2,3,4}累加,若結果大於7我們才返回真實值。那麼我們可以這樣寫:

@Test
public void reduceTest() {
    Integer testInt[]={1,2,3,4};
    Optional<Integer> sumAll = Stream.of(testInt).reduce(Integer::sum);
    System.out.println(sumAll.filter(x -> x>7));//Optional[10]
}

若計算結果是6,那麼打印的值會是Optional.empty。當然還可以進行轉換,比如下面是,把小寫轉換成大寫的代碼:

@Test
public void reduceTest() {
    String testS[]={"hello"," ","world"," ","!"};
    Optional<String> sumAll = Stream.of(testS).reduce(String::concat);
    System.out.println(sumAll.map(x-> null));//Optional.empty
    System.out.println(sumAll.map(x-> x.toUpperCase()));//Optional[HELLO WORLD !]
}

這裏用到了map就得說說OptionalflatMap了,其實flatMapmap的操作非常相似,但是呢,flatMap裏面傳入的東西必須是Optional對象。我們看到map可以返回null可以返回x.toUpperCase(),但是flatMap是不可以的。上面的例子我們改由flatMap,就該這麼寫:

@Test
public void reduceTest() {
    String testS[]={"hello"," ","world"," ","!"};
    Optional<String> sumAll = Stream.of(testS).reduce(String::concat);
    System.out.println(sumAll.flatMap(x-> Optional.ofNullable(null)));//Optional.empty
    System.out.println(sumAll.flatMap(x-> Optional.of(x.toUpperCase())));//Optional[HELLO WORLD !]
}
好,至此,Optional裏面的方法就算講完一遍了,沒講的,都是從Object類繼承下來的方法,沒啥好講的了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章