一句話介紹Optional類:使用JDK8的Optional類來防止NullPointerException(空指針異常)問題
。html
在咱們開放過程當中,碰到的異常中NullPointerException必然是排行第一的。因此在平時編碼中,咱們會時時的判斷null。java
public void saveCity(City city) { if (city != null) { String cityName = city.getCityName(); if (cityName != null) { String code = cityDao.findCodeByName(cityName); city.setCode(code); cityDao.save(city); } } }
雖然上面代碼變得更加安全,可是過多嵌套 if 語句下降代碼總體可讀性,提升複雜度。咱們能夠優化下代碼編程
public void saveCity(City city) { if (city == null) { return; } String cityName = city.getCityName(); if (cityName == null) { return; } String code = cityDao.findCodeByName(cityName); city.setCode(code); cityDao.save(city); }
這樣還能夠,但咱們經過Optional變的更簡潔api
public void saveCity(City city) { //就一行 city不爲空返回 城市名稱 不然直接返回空 Optional<String> roleOpt = Optional.ofNullable(city).map(City::getCityName); //若是容器中 不爲空 if (roleOpt.isPresent()) { String code = cityDao.findCodeByName(roleOpt.get()); city.setCode(code); cityDao.save(city); } }
這樣,咱們僅須要對咱們關心的作一次校驗,省卻了前面的一系列的檢驗操做。安全
概念
Optiona本質是一個容器,容器中存在爲null或者不包含非null值的容器對象。提供了一系列的方法供咱們判斷該容器裏的對象是否存在。app
/** * final修飾表明不能被子類繼承 */ public final class Optional<T> { /** * 建立一個空容器 */ private static final java.util.Optional<?> EMPTY = new java.util.Optional<>(); /** * 傳入的值 */ private final T value; /** * 構造函數私有化 說明不能被外部new */ private Optional() { this.value = null; } /** * 私有化構造函數 */ private Optional(T value) { this.value = Objects.requireNonNull(value); } /** * 獲取空容器 */ public static <T> java.util.Optional<T> empty() { @SuppressWarnings("unchecked") java.util.Optional<T> t = (java.util.Optional<T>) EMPTY; return t; } /** * 傳入的對象不能爲空 不然拋異常 */ public static <T> java.util.Optional<T> of(T value) { return new java.util.Optional<>(value); } /** * 傳入的對象能夠爲空 */ public static <T> java.util.Optional<T> ofNullable(T value) { return value == null ? empty() : of(value); } /** * 獲取容器對象的方法 注意 若是用這個方法則表明容器中必定有對象,不然拋異常 */ public T get() { if (value == null) { throw new NoSuchElementException("No value present"); } return value; } /** * 判斷容器對象是否爲空 */ public boolean isPresent() { return value != null; } /** * 若是容器對象爲空 則返回當前對象 */ public T orElse(T other) { return value != null ? value : other; } //==========有關下面這幾個JDK8自帶的函數式接口的做用,上一篇博客有詳細說明,這裏就很少說了。 /** * 傳入Consumer編程式接口參數 */ public void ifPresent(Consumer<? super T> consumer) { if (value != null) consumer.accept(value); } /** * 傳入Predicate編程式接口參數 */ public java.util.Optional<T> filter(Predicate<? super T> predicate) { Objects.requireNonNull(predicate); if (!isPresent()) return this; else return predicate.test(value) ? this : empty(); } /** * 傳入Function編程式接口參數 */ public <U> java.util.Optional<U> map(Function<? super T, ? extends U> mapper) { Objects.requireNonNull(mapper); if (!isPresent()) return empty(); else { return java.util.Optional.ofNullable(mapper.apply(value)); } } /** * 傳入Function編程式接口參數 */ public <U> java.util.Optional<U> flatMap(Function<? super T, java.util.Optional<U>> mapper) { Objects.requireNonNull(mapper); if (!isPresent()) return empty(); else { return Objects.requireNonNull(mapper.apply(value)); } } /** * 傳入Supplier編程式接口參數 */ public T orElseGet(Supplier<? extends T> other) { return value != null ? value : other.get(); } /** * 傳入Supplier編程式接口參數 */ public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X { if (value != null) { return value; } else { throw exceptionSupplier.get(); } } }
經過上面源碼能夠看出,Optional的構造函數都是私有化的,沒法直接new對象。它這邊提供了3個靜態方法獲取對象。函數
一、建立一個必定是空的Optional容器
優化
Optional<Car> optCar = Optional.empty();
二、建立一個必定是非空值Optional容器
(傳入的對象不能夠爲null,不然拋出NullPointerException)ui
Optional<Car> optUser = Optional.of(user);
三、建立一個多是空也可能不爲空的Optional容器
(傳入的對象能夠爲null)this
Optional<Car> optUser = Optional.ofNullable(user);
一、isPresent() //有值則返回true 二、get(): //值存在時返回值,不然拋出一個NoSuchElement異常(因此調這個,通常先判斷上面方法返回是否爲true) 三、orElse(T other) //值存在時返回值,不然返回一個默認值 四、ifPresent(Consumer<T> block) //會在值存在的時候執行給定的代碼塊 五、orElseThrow(Supplier<? extends X> exceptionSupplier) //與get()相似,不一樣的是能夠自定義異常類型 六、orElseGet(Supplier<? extends T> other) //orElse方法的延遲調用版,Supplier方法只有在Optional對象不含值時才執行調用 七、map/flatMap/filter //與Stream中用法相似
這裏寫一個針對以上API都涉及到的Demo,這個例子明白了,那麼Optional的使用也就都清楚了。
代碼
public class OptionalDemo { public static void main(String[] args) { //一、建立Optional實例,傳入的對象不能爲null Optional<String> nameOptional = Optional.of("張三"); //二、建立Optional實例,傳入對象能夠爲null,也能夠不weinull Optional emptyOptional = Optional.ofNullable(null); //三、isPresent方法用來檢查Optional實例是否有值。 if (nameOptional.isPresent()) { //調用get()返回Optional值。 System.out.println("一、" + nameOptional.get()); } try { //四、在Optional實例上調用get()拋出NoSuchElementException。 System.out.println("二、" + emptyOptional.get()); } catch (NoSuchElementException ex) { System.out.println("三、異常" + ex.getMessage()); } // //五、若是Optional值不爲空,lambda表達式會處理並在其上執行操做。(這裏x表明就是nameOptional中的對象) nameOptional.ifPresent((x) -> { System.out.println("四、字符串長度爲: " + x.length()); }); //六、若是有值orElse方法會返回Optional實例,沒值則返回當前值 System.out.println("五、"+ emptyOptional.orElse("若是是空容器則返回李四")); System.out.println("六、"+nameOptional.orElse("若是是空容器則返回王五")); //七、orElseGet與orElse相似,區別在於傳入的參數不一樣,一個是直接傳入對象,這個是傳入Supplier函數式接口 System.out.println("七、" + emptyOptional.orElseGet(() -> "李四")); System.out.println("八、" + nameOptional.orElseGet(() -> "王五")); try { //八、若是是空容器,則能夠拋出自定義異常。 emptyOptional.orElseThrow(() -> new NullPointerException("空容器異常")); } catch (Throwable ex) { System.out.println("九、" + ex.getMessage()); } Optional<String> ageOptional = Optional.of("10"); //九、這裏入參是Function,因此能夠轉換容器中的對象 比如將String對象轉爲Integer對象 Optional<Integer> age = ageOptional.map((value) -> Integer.parseInt(value)); /** * 十、flatMap與map(Funtion)很是類似,不一樣在於 map返回能夠將String對象轉爲Integer對象,但flatMap轉換後必定仍是String對象 */ Optional<String> upperName = nameOptional.flatMap((value) -> Optional.of(value.toUpperCase())); //十一、filter方法檢查Optiona值是否知足給定條件。若是知足返回Optional實例值,不然返回空Optional。 Optional<String> longName = nameOptional.filter((value) -> value.length() > 6); System.out.println("十、" + longName.orElse("longName容器的名字長度小於6位")); //十二、另外一個示例,Optional知足給定條件。 Optional<String> anotherName = Optional.of("烏啦啦市長公主"); Optional<String> shortName = anotherName.filter((value) -> value.length() > 6); System.out.println("十一、" + shortName.orElse("anotherName容器的名字長度小於6位")); } }
運行結果
你若是願意有所做爲,就必須善始善終。(26)