只有光头才能变强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一个出来啊。
固然了,个人女友是现实存在的。
参考资料:
若是你以为我写得还不错,了解一下: