只有光頭才能變強html
前兩天帶女友去圖書館了,隨手就給她來了一本《與孩子一塊兒學編程》的書,因而今天就給女友講解一下什麼是Optional類。java
不知道你們還記得上一篇《阿里巴巴 Java開發手冊》讀後感不,當時閱讀到空指針異常(NPE)時,書上提到JDK 8有個Optional類供咱們使用,該類能夠儘量地防止出現空指針異常(NPE)。git
文本力求簡單講清每一個知識點,但願你們看完能有所收穫程序員
咱們都知道JDK 8最重要的新特性是Lambda表達式,這個可讓咱們簡化很是多的代碼編寫,不知道你們會使用了沒有。這裏我簡單跟你們來回顧一下~github
下面就以幾個例子來看看Lambda表達式是怎麼簡化咱們代碼的編寫的。編程
首先咱們來看看建立線程:安全
public static void main(String[] args) { // 用匿名內部類的方式來建立線程 new Thread(new Runnable() { @Override public void run() { System.out.println("公衆號:Java3y---回覆1進羣交流"); } }); // 使用Lambda來建立線程 new Thread(() -> System.out.println("公衆號:Java3y---回覆1進羣交流")); }
再來看看遍歷Map集合:多線程
public static void main(String[] args) { Map<String, String> hashMap = new HashMap<>(); hashMap.put("公衆號", "Java3y"); hashMap.put("羣", "回覆1"); // 使用加強for的方式來遍歷hashMap for (Map.Entry<String, String> entry : hashMap.entrySet()) { System.out.println(entry.getKey()+":"+entry.getValue()); } // 使用Lambda表達式的方式來遍歷hashMap hashMap.forEach((s, s2) -> System.out.println(s + ":" + s2)); }
在List中刪除某個元素app
public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("Java3y"); list.add("3y"); list.add("光頭"); list.add("帥哥"); // 傳統的方式刪除"光頭"的元素 ListIterator<String> iterator = list.listIterator(); while (iterator.hasNext()) { if ("光頭".equals(iterator.next())) { iterator.remove(); } } // Lambda方式刪除"光頭"的元素 list.removeIf(s -> "光頭".equals(s)); // 使用Lambda遍歷List集合 list.forEach(s -> System.out.println(s)); }
從上面的例子咱們能夠看出,Lambda表達式的確是能夠幫咱們簡化代碼的。ide
使用Lambda表達式,其實都是創建在函數式接口上的。咱們看看上面的代碼的接口:
建立多線程的Runnable接口:
@FunctionalInterface public interface Runnable { public abstract void run(); }
遍歷HashMap的BiConsumer接口:
@FunctionalInterface public interface BiConsumer<T, U> { void accept(T t, U u); default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after) { Objects.requireNonNull(after); return (l, r) -> { accept(l, r); after.accept(l, r); }; } }
在List中刪除元素的Predicate接口:
@FunctionalInterface public interface Predicate<T> { boolean test(T t); default Predicate<T> and(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) && other.test(t); } default Predicate<T> negate() { return (t) -> !test(t); } default Predicate<T> or(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) || other.test(t); } static <T> Predicate<T> isEqual(Object targetRef) { return (null == targetRef) ? Objects::isNull : object -> targetRef.equals(object); } }
函數式接口的特色:由@FunctionalInterface
註解標識,接口有且僅有一個抽象方法!
或許咱們一開始看到Lambda的時候,發現Lambda表達式的語法有點奇葩,甚至有點看不懂。沒事,這裏3y給你們用圖的形式畫一畫:
以Runnable接口來舉例:
再不濟,咱們在用IDE的時候,能夠提示出Lambda表達式的語法的,這樣能夠幫咱們快速上手Lambda表達式:
說白了,咱們使用Lambda表達式的架子是這樣的()->{}
,具體的時候看看函數式接口的抽象方法要求就能夠了,再不濟就使用IDE智能提示。
好比說public<U> Optional<U> map(Function<? super T, ? extends U> mapper)
這個聲明,你看懂了嗎?
// 接口 @FunctionalInterface public interface Function<T, R> { R apply(T t); }
在泛型的上限和下限中有一個原則:PECS(Producer Extends Consumer Super)
解析:傳入的參數是泛型 T 或者其父類,返回值是U或其子類。
具體可參考:
一句話介紹Optional類:使用JDK8的Optional類來防止NPE(空指針異常)問題。
接下來咱們看看文檔是怎麼說的:
A container object which may or may not contain a non-null value.Additional methods that depend on the presence or absence of a contained value are provided
它是一個容器,裝載着非NULL元素(或者沒有裝載元素),提供了一系列的方法供咱們判斷該容器裏的對象是否存在(以及後續的操做)。
Optional類的方法結構圖:
咱們先來看看Optional的屬性以及建立Optional容器的方法:
// 一、建立出一個Optional容器,容器裏邊並無裝載着對象 private static final Optional<?> EMPTY = new Optional<>(); // 二、表明着容器中的對象 private final T value; // 三、私有構造方法 private Optional() { this.value = null; } // 四、獲得一個Optional容器,Optional沒有裝載着對象 public static<T> Optional<T> empty() { @SuppressWarnings("unchecked") Optional<T> t = (Optional<T>) EMPTY; return t; } // 五、私有構造方法(帶參數),參數就是具體的要裝載的對象,若是傳進來的對象爲null,拋出異常 private Optional(T value) { this.value = Objects.requireNonNull(value); } // 5.一、若是傳進來的對象爲null,拋出異常 public static <T> T requireNonNull(T obj) { if (obj == null) throw new NullPointerException(); return obj; } // 六、建立出Optional容器,並將對象(value)裝載到Optional容器中。 // 傳入的value若是爲null,拋出異常(調用的是Optional(T value)方法) public static <T> Optional<T> of(T value) { return new Optional<>(value); } // 建立出Optional容器,並將對象(value)裝載到Optional容器中。 // 傳入的value能夠爲null,若是爲null,返回一個沒有裝載對象的Optional對象 public static <T> Optional<T> ofNullable(T value) { return value == null ? empty() : of(value); }
因此能夠得出建立Optional容器有兩種方式:
下面咱們簡單就能夠看看用法了:
如今我有一個User對象,這裏用到了Lombok,有興趣的同窗可去學學瞭解一下:兩個月的Java實習結束,繼續努力
import lombok.Data; @Data public class User { private Integer id; private String name; private Short age; }
測試:
public static void main(String[] args) { User user = new User(); User user1 = null; // 傳遞進去的對象不能夠爲null,若是爲null則拋出異常 Optional<User> op1 = Optional.of(user1); // 傳遞進去的對象能夠爲null,若是爲null則返回一個沒有裝載對象的Optional容器 Optional<User> op2 = Optional.ofNullable(user); }
// 獲得容器中的對象,若是爲null就拋出異常 public T get() { if (value == null) { throw new NoSuchElementException("No value present"); } return value; } // 判斷容器中的對象是否爲null public boolean isPresent() { return value != null; } // 若是容器中的對象存在,則返回。不然返回傳遞進來的參數 public T orElse(T other) { return value != null ? value : other; }
這三個方法是Optional類比較經常使用的方法,而且是最簡單的。(由於參數不是函數式接口)
下面咱們繼續看看用法:
public static void main(String[] args) { User user = new User(); User user1 = null; Optional<User> op1 = Optional.ofNullable(user); System.out.println(op1.isPresent()); System.out.println(op1.get()); System.out.println(op1.orElse(user1)); }
結果很明顯,由於咱們的user是不爲null的:
咱們調換一下順序看看:
public static void main(String[] args) { User user = new User(); User user1 = null; Optional<User> op1 = Optional.ofNullable(user1); System.out.println(op1.isPresent()); System.out.println(op1.orElse(user)); System.out.println(op1.get()); }
固然了,咱們到目前爲止看起來Optional類好像就這麼一回事了,這樣代碼寫起來還不如我本身判斷null呢...
咱們對比一下:
咱們能夠發現,手動判斷是否爲null好像還更方便簡潔一點呢。
因此,咱們帶函數式接口的方法登場了!
首先來看看ifPresent(Consumer<? super T> consumer)
方法
public void ifPresent(Consumer<? super T> consumer) { if (value != null) consumer.accept(value); } @FunctionalInterface public interface Consumer<T> { void accept(T t); }
若是容器中的對象存在,則調用accept方法,好比說:
public static void main(String[] args) { User user = new User(); user.setName("Java3y"); test(user); } public static void test(User user) { Optional<User> optional = Optional.ofNullable(user); // 若是存在user,則打印user的name optional.ifPresent((value) -> System.out.println(value.getName())); // 舊寫法 if (user != null) { System.out.println(user.getName()); } }
直接看源碼:
// 若是對象存在,則直接返回,不然返回由Supplier接口的實現用來生成默認值 public T orElseGet(Supplier<? extends T> other) { return value != null ? value : other.get(); } @FunctionalInterface public interface Supplier<T> { T get(); } // 若是存在,則返回。不然拋出supplier接口建立的異常 public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X { if (value != null) { return value; } else { throw exceptionSupplier.get(); } }
例子:
public static void main(String[] args) { User user = new User(); user.setName("Java3y"); test(user); } public static void test(User user) { Optional<User> optional = Optional.ofNullable(user); // 若是存在user,則直接返回,不然建立出一個新的User對象 User user1 = optional.orElseGet(() -> new User()); // 舊寫法 if (user != null) { user = new User(); } }
總的來講跟咱們上面所講的orElse()
差很少,只不過它能夠經過Supplier接口的實現來生成默認值。
直接看源碼:
// 若是容器中的對象存在,而且符合過濾條件,返回裝載對象的Optional容器,不然返回一個空的Optional容器 public Optional<T> filter(Predicate<? super T> predicate) { Objects.requireNonNull(predicate); if (!isPresent()) return this; else return predicate.test(value) ? this : empty(); } // 接口 @FunctionalInterface public interface Predicate<T> { boolean test(T t); }
返回Optional對象咱們就能夠實現鏈式調用了!
例子:
public static void test(User user) { Optional<User> optional = Optional.ofNullable(user); // 若是容器中的對象存在,而且符合過濾條件,返回裝載對象的Optional容器,不然返回一個空的Optional容器 optional.filter((value) -> "Java3y".equals(value.getName())); }
直接看源碼:
// 若是容器的對象存在,則對其執行調用mapping函數獲得返回值。而後建立包含mapping返回值的Optional,不然返回空Optional。 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)); } } // 接口 @FunctionalInterface public interface Function<T, R> { R apply(T t); }
例子:
public static void test(User user) { Optional<User> optional = Optional.ofNullable(user); // 若是容器的對象存在,則對其執行調用mapping函數獲得返回值。而後建立包含mapping返回值的Optional,不然返回空Optional。 optional.map(user1 -> user1.getName()).orElse("Unknown"); } // 上面一句代碼對應着最開始的老寫法: public String tradition(User user) { if (user != null) { return user.getName(); }else{ return "Unknown"; } }
直接看源碼:
// flatMap方法與map方法相似,區別在於apply函數的返回值不一樣。map方法的apply函數返回值是? extends U,而flatMap方法的apply函數返回值必須是Optional public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) { Objects.requireNonNull(mapper); if (!isPresent()) return empty(); else { return Objects.requireNonNull(mapper.apply(value)); } }
再來感覺一下Optional的魅力
public static void main(String[] args) { User user = new User(); user.setName("Java3y"); System.out.println(test(user)); } // 之前的代碼v1 public static String test2(User user) { if (user != null) { String name = user.getName(); if (name != null) { return name.toUpperCase(); } else { return null; } } else { return null; } } // 之前的代碼v2 public static String test3(User user) { if (user != null && user.getName() != null) { return user.getName().toUpperCase(); } else { return null; } } // 如今的代碼 public static String test(User user) { return Optional.ofNullable(user) .map(user1 -> user1.getName()) .map(s -> s.toUpperCase()).orElse(null); }
Optional總結:
filter,map或flatMap一個函數,函數的參數拿到的值必定不是null。因此咱們經過filter,map 和 flatMap之類的函數能夠將其安全的進行變換,最後經過orElse系列,get,isPresent 和 ifPresent將其中的值提取出來。
其實吧,用Optional類也沒有簡化不少的代碼,只是把NPE異常經過各類方法隱藏起來(包裝了一層)。經過Lambda表達式可讓咱們處理起來更加"優雅"一些。
以前在初學的時候沒在乎JDK8的特性,其實JDK更新不少時候都能給咱們帶來很多好處的(簡化代碼編寫,提升性能等等),因此做爲一名Java程序員,仍是得多學學新特性。(話說JDK9該類又有新特性了...)
若是你要評論「醒醒吧,程序員哪來的女友」,「我尿黃,讓我來」之類的話,我建議你是否是好好檢討一下本身,爲何別的程序員都有女友,就你沒有,是否是本身技術不過關了?經過「工廠」找一個有那麼難嗎?再不濟也能本身new一個出來啊。
固然了,個人女友是現實存在的。
參考資料:
若是你以爲我寫得還不錯,瞭解一下: