java中不太常見的東西(3) - Optional

引言

上一篇博文我們主要介紹了關於Lambda表達式相關的知識,所以在今天的博文中我想把一個同樣還不普及的類介紹給大家,Optional這個類是在Jdk1.8的時候引入的,它的主要功能是判斷一個值是否爲空,伴隨着對數據相應的處理。筆者目前整理的一些blog針對面試都是超高頻出現的。大家可以點擊鏈接:http://blog.csdn.net/u012403290

技術點

1、靜態工廠
我們在創建一個對象實例的時候,基本上都是靠構造器進行創建的。其實它還有一個更普及,但是你可能沒發現的方法。它就是靜態工廠方法, 它在類中是以一個靜態方法的形態存在,比如說Intger類中有這麼一段源碼:

 public static Integer valueOf(int i) {//一個靜態工廠方法
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

從上面的方法中,我們就可以直接返回一個Integer的實例。這種靜態工廠方法相對於一般構造器來說有着諸多的優點,這裏不一一展開,有興趣可以參考《effective java》中的第一條。

O**ptional源碼解讀**

1、類介紹

public final class Optional<T> {
}

從類定義上來看,我們可以看出亮點:a、它是一個不可擴展類,和String一樣,是用final 修飾的,不能被繼承,方法也不能被修改,因爲final類中的方法都是默認final的;b、是一個泛型類,這個也是合理的,因爲前面說過Optional的核心功能是對數據進行判斷,因爲對數據的未知,所以泛型的設計顯得非常必要。

2、實例創建
以下是Optional的兩個構造函數:

    /**
     * Constructs an empty instance.
     *
     * @implNote Generally only one empty instance, {@link Optional#EMPTY},
     * should exist per VM.
     */
    private Optional() {//私有的不帶參的構造函數
        this.value = null;
    }

  /**
     * Constructs an instance with the value present.
     *
     * @param value the non-null value to be present
     * @throws NullPointerException if value is null
     */
    private Optional(T value) {//私有的帶參構造函數
        this.value = Objects.requireNonNull(value);
    }

構造函數被定義成private的,說明這個類是不能通過構造函數進行實例化的。但是發現它具有幾個可以創建實例等靜態工廠方法:

//1
    public static<T> Optional<T> empty() {//empty方法,可以反悔一個空的Optional對象
        @SuppressWarnings("unchecked")
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }
//2
  public static <T> Optional<T> of(T value) {//of方法,可以傳入一個value值,並返回一個帶有value值的Optional對象
        return new Optional<>(value);
    }
//3
    public static <T> Optional<T> ofNullable(T value) {//ofNullable方法,如果傳入爲空就返回一個空的Optional對象,如果傳入值不爲空就返回一個帶有value的Optional對象
        return value == null ? empty() : of(value);
    }

所以Optional對象不可以通過構造器的方式進行實例化,可以通過靜態工廠方法創建一個對應的Optional實例。

3、關鍵方法

a、get

    /**
     * If a value is present in this {@code Optional}, returns the value,
     * otherwise throws {@code NoSuchElementException}.
     *
     * @return the non-null value held by this {@code Optional}
     * @throws NoSuchElementException if there is no value present
     *
     * @see Optional#isPresent()
     */
    public T get() {//獲取Optional的值
        if (value == null) {//如果值爲null拋出一個錯誤
            throw new NoSuchElementException("No value present");
        }
        return value;
    }

get方法主要就是如果Optional中如果存在值就返回,爲null就拋出一個錯誤。

b、isPresent

     * Return {@code true} if there is a value present, otherwise {@code false}.
     *
     * @return {@code true} if there is a value present, otherwise {@code false}
     */
    public boolean isPresent() {
        return value != null;//如果值存在返回true,否則返回false
    }

是一個boolean的方法,它主要是判Optional中的值是否存在,如果存在就返回true,如果不存在就返回false。

c、ifPresent

    /**
     * If a value is present, invoke the specified consumer with the value,
     * otherwise do nothing.
     *
     * @param consumer block to be executed if a value is present
     * @throws NullPointerException if value is present and {@code consumer} is
     * null
     */
    public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)//如果值是否存在
            consumer.accept(value);//值存在就需要調用consumer進行處理
    }

關於consumer在昨天的Lambda表達式中我們就介紹過了,它是一個沒有返回的函數接口。具體的我們在下面的例子中可以見到。

d、orElse

    /**
     * Return the value if present, otherwise return {@code other}.
     *
     * @param other the value to be returned if there is no value present, may
     * be null
     * @return the value, if present, otherwise {@code other}
     */
  public T orElse(T other) {
        return value != null ? value : other;//如果值存在則返回這個值,如果值不存在就返回輸入
    }

這個方法帶有一個類型是輸入,如果Optional的值不爲空,則返回這個值,如果爲空那麼就返回輸入。

e、filter

   /**
     * If a value is present, and the value matches the given predicate,
     * return an {@code Optional} describing the value, otherwise return an
     * empty {@code Optional}.
     *
     * @param predicate a predicate to apply to the value, if present
     * @return an {@code Optional} describing the value of this {@code Optional}
     * if a value is present and the value matches the given predicate,
     * otherwise an empty {@code Optional}
     * @throws NullPointerException if the predicate is null
     */
    public Optional<T> filter(Predicate<? super T> predicate) {
        Objects.requireNonNull(predicate);
        if (!isPresent())
            return this;
        else
            return predicate.test(value) ? this : empty();//進行一次判斷處理並返回
    }

predicate在上一篇博文中也介紹過了,它是一個boolean的參數接口,可以進行條件判斷。具體在demo中會掩飾。

f、map


    public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Optional.ofNullable(mapper.apply(value));//用function對value值進行修改
        }
    }

Function的函數接口在上一篇博文中也介紹過了,它主要是傳入一個類型的數據,返回一個類型的數據。在上面的源碼種可以看出,返回的類型也是一個Optional的數據。

其實它還有幾個方法和Object的方法,因爲在上一片博文中沒有設計過的東西,這裏就不再展開。

一個完整的例子

以下是我寫的詳細demo:

package com.bw;

import java.util.NoSuchElementException;
import java.util.Optional;

public class OptionalTest {
     public static void main(String[] args) {
            /**
             * 3種靜態工廠生產Optional對象
             */

            //創建一個值爲brickworker的Optional對象
            Optional<String> o1 = Optional.of("brickworker");

            //創建一個空的Optional
            Optional<String> o2 = Optional.empty();

            //創建一個空的Optional
            Optional<String> o3 = Optional.ofNullable(null);

            //創建一個值爲brickworker的Optional對象
            Optional<String> o4 = Optional.ofNullable("brickworker");


            //get方法
            System.out.println("=========get方法==========");
            System.out.println("o1存在值"+o1.get());
            try {
                 System.out.println("o2存在值"+o2.get());//o2和o3是空,會拋出錯誤,專門處理
            } catch (Exception e) {//不必打印錯誤
            }

            try {
                 System.out.println("o3存在值"+o3.get());//o2和o3是空,會拋出錯誤,專門處理
            } catch (Exception e) {//不必打印錯誤
            System.out.println("o4存在值"+o4.get());
            }


            System.out.println("========isPresent方法=========");
            if(o1.isPresent()){
                System.out.println("o1有數據");
            }
            if(o2.isPresent()){
                System.out.println("o2有數據");
            }
            if(o3.isPresent()){
                System.out.println("o3有數據");
            }
            if(o4.isPresent()){
                System.out.println("o4有數據");
            }


            System.out.println("===========orElse方法===========");

            System.out.println(o1.orElse("o1沒值輸出這個"));
            System.out.println(o2.orElse("o2沒值輸出這個"));
            System.out.println(o3.orElse("o3沒值輸出這個"));
            System.out.println(o4.orElse("o4沒值輸出這個"));


            System.out.println("========map方法=================");

            Optional<String> no1 = o1.map(v -> v.toUpperCase());
            System.out.println(no1.orElse("new o1如果沒值輸出這個"));
            Optional<String> no2 = o2.map(v -> v.toUpperCase());
            System.out.println(no2.orElse("new o2如果沒值輸出這個"));
            Optional<String> no3 = o3.map(v -> v.toUpperCase());
            System.out.println(no3.orElse("new o3如果沒值輸出這個"));
            Optional<String> no4 = o4.map(v -> v.toUpperCase());
            System.out.println(no4.orElse("new o4如果沒值輸出這個"));


            System.out.println("============filter方法==============");
            Optional<String> nno1 = o1.filter(v -> v.indexOf("brick") > -1);//如果字符串中存在brick
            System.out.println(nno1.orElse("nnew o1如果沒值輸出這個"));
            Optional<String> nno2 = o2.filter(v -> v.indexOf("brick") > -1);//如果字符串中存在brick
            System.out.println(nno2.orElse("nnew o1如果沒值輸出這個"));
            Optional<String> nno3 = o3.filter(v -> v.indexOf("brick") > -1);//如果字符串中存在brick
            System.out.println(nno3.orElse("nnew o1如果沒值輸出這個"));
            Optional<String> nno4 = o4.filter(v -> v.indexOf("brick") > -1);//如果字符串中存在brick
            System.out.println(nno4.orElse("nnew o1如果沒值輸出這個"));


            System.out.println("==========ifPresent方法==========");
            o1.ifPresent(v -> {System.out.println("o1:"+v);});
            o2.ifPresent(v -> {System.out.println("o2:"+v);});
            o3.ifPresent(v -> {System.out.println("o3:"+v);});
            o4.ifPresent(v -> {System.out.println("o4:"+v);});

     }
}

//輸出:
//=========get方法==========
//o1存在值brickworker
//o4存在值brickworker
//========isPresent方法=========
//o1有數據
//o4有數據
//===========orElse方法===========
//brickworker
//o2沒值輸出這個
//o3沒值輸出這個
//brickworker
//========map方法=================
//BRICKWORKER
//new o2如果沒值輸出這個
//new o3如果沒值輸出這個
//BRICKWORKER
//============filter方法==============
//brickworker
//nnew o1如果沒值輸出這個
//nnew o1如果沒值輸出這個
//brickworker
//==========ifPresent方法==========
//o1:brickworker
//o4:brickworker

關於Optional類就介紹到這裏,如果對於中間出現的Lambda表達式如果有不清楚的,請參考我上一篇博文。

以後大家在對輸入值進行判斷的時候,就可以用Optional來進行控制,從而防止NPE問題的產生。希望這篇博文對大家所有幫助,如果存在錯誤請幫忙指出。

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