Java 8 新特性:Java 类库的新特性之 Optional类

简介: Java 8 新特性:Java 类库的新特性之 Optional类

Java 类库的新特性之 Optional类


1.Optional简述


到目前为止,著名的NullPointerException是导致Java应用程序失败的最常见原因。过去,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。如今,受到Google Guava的启发,Optional类已经成为Java 8类库的一部分。


Optional:按照字面英文解释为“可选的” 意思,但此处的语义是指某个值可能有也可能没有(null)。


Optional 被定义为一个简单的容器,其值可能是null或者不是null。在Java 8之前一般某个函数应该返回非空对象但是偶尔却可能返回了null,而在Java 8 以后,不推荐你返回null而是返回Optional。


Optional英文文档地址:http://docs.oracle.com/javase/8/docs/api/java/util/Optional.html


2.对 Optional 应用的理解


Java 8借鉴了Scala和Haskell,提供了一个新的Optional模板,可以用它来封装可能为空的引用。但它绝不是终结空指针,更多只是使API的设计者可以在代码层面声明一个方法可能会返回空值,调用方应该注意这种情况。因此,这只对新的API有效,前提是调用者不要让引用逃逸出封装类,否则引用可能会在外面被不安全的废弃掉。


个人对这个新的特性是又爱又恨。一方面,空指针是一个大问题,只要能解决这个问题的东西笔者都欢迎。但另一方面,个人对它是否能担此重任执怀疑的态度。这是由于使用它需要全公司的集体努力,短期内很难会有见效。若非大力地推广使用,很可能会功亏一篑。


3.Optional的优点


1)显式的提醒你需要关注null的情况,对程序员是一种字面上的约束

2)将平时的一些显式的防御性检测给标准化了,并提供一些可串联操作

3)解决null会导致疑惑的概念


eg:Map里面的key==null的情况,以及value==null的情况


4.Optional类


4.1 Optional类的官方描述


A container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the value.


(这是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。)


4.2 Optional的简单使用


Optional类的包名:java.util.Optional<T>


为了防止抛出java.lang.NullPointerException 异常,我们经常会写出这样的代码:

Student student = person.find("Lucy");
    if (student != null) {
      student.doSomething();
    }

 

使用 Optional的代码写法:

Student student = person.find("Lucy");
    if (student.isPresent()) {
      student.get().doSomething();
    }

说明:如果isPresent()返回false,说明这是个空对象;否则,我们就可以把其中的内容取出来做相应的操作了。

单从代码量上来说,Optional的代码量并未减少,甚至比原来的代码还多。但好在,你绝对不会忘记判空,因为这里我们得到的不是Student类的对象,而是Optional。



4.3Optional类的主要方法


1) of(T value)


为非null的值创建一个Optional。


of()方法通过工厂方法创建Optional类。需要注意,创建对象时传入的参数不能为null。如果传入参数为null,则抛出NullPointerException 。


eg:


  //调用工厂方法创建Optional实例
    Optional<String> myValue = Optional.of("OptionalTest");
    //传入参数为null,抛出NullPointerException.
    Optional<String> someNull = Optional.of(null);

2) ofNullable(T value)


为指定的值创建一个Optional,如果指定的值为null,则返回一个空的Optional。

ofNullable与of方法相似,唯一的区别是可以接受参数为null的情况。

eg:

    //下面创建了一个不包含任何值的Optional实例
    //eg:值为'null'
    Optional empty = Optional.ofNullable(null);

3) isPresent()

如果值存在返回true,否则返回false。

eg:

    //isPresent()方法用来检查Optional实例中是否包含值
    if (myValue.isPresent()) {
    //在Optional实例内调用get()返回已存在的值
    System.out.println(myValue.get());//输出OptionalTest }

4) get()


如果Optional有值则将其返回,否则抛出异常 NoSuchElementException。


在上面的示例中,get方法用来得到Optional实例中的值。下面是一个抛出NoSuchElementException的示例:


eg:


//执行下面的代码会输出:No value present 
    try {
      //在空的Optional实例上调用get(),抛出NoSuchElementException
      System.out.println(empty.get());
    } catch (NoSuchElementException ex) {
      System.out.println(ex.getMessage());
    }

5) ifPresent(Consumer<? super T> consumer)


如果Optional实例有值则为其调用consumer,否则不做处理。


要理解ifPresent()方法,首先需要了解Consumer类。简单地说,Consumer类包含一个抽象方法。该抽象方法对传入的值进行处理,但没有返回值。Java8支持不用接口直接通过lambda表达式传入参数。


如果Optional实例有值,调用ifPresent()可以接受接口段或lambda表达式。


eg:

//ifPresent()方法接受lambda表达式作为参数。
    //lambda表达式对Optional的值调用consumer进行处理。
    myValue.ifPresent((value) -> {
      System.out.println("The length of the value is: " + value.length());
    });

6) orElse(T other)

如果有值则将其返回,否则返回指定的其它值。
如果Optional实例有值则将其返回,否则返回orElse方法传入的参数。即:参数other为默认返回值

eg:

//如果值不为null,orElse方法返回Optional实例的值。
    //如果为null,返回传入的消息。
    //输出:There is no value present!
    System.out.println(empty.orElse("There is no value present!"));
    //输出:OptionalTest
    System.out.println(myValue.orElse("There is some value!"));

7) orElseGet(Supplier<? extends T> other)


orElseGet与orElse方法类似,区别在于得到的默认值。orElse方法将传入的参数字符串作为默认值,orElseGet方法可以接受Supplier接口的实现用来生成默认值。


eg:

//orElseGet可以接受一个lambda表达式生成默认值。
    //输出:Default Value
    System.out.println(empty.orElseGet(() -> "Default Value"));
    //输出:OptionalTest
    System.out.println(myValue.orElseGet(() -> "Default Value"));

8) orElseThrow(Supplier<? extends X> exceptionSupplier)


如果有值则将其返回,否则抛出supplier接口创建的异常。

在orElseGet()方法中,我们传入一个Supplier接口。然而,在orElseThrow中我们可以传入一个lambda表达式或方法,如果值不存在就抛出异常。


eg:

try {
      //orElseThrow与orElse方法类似。与返回默认值不同,
      //orElseThrow会抛出lambda表达式或方法生成的异常 
      empty.orElseThrow(ValueAbsentException::new);
    } catch (Throwable ex) {
      //输出: No value present in the Optional instance
      System.out.println(ex.getMessage());
    }
class ValueAbsentException extends Throwable {
  public ValueAbsentException() {
    super();
  }
  public ValueAbsentException(String msg) {
    super(msg);
  }
  @Override
  public String getMessage() {
    return "No value present in the Optional instance";
  }
}

9) map(Function<? super T,? extends U> mapper)


如果有值,则对其执行调用mapping函数得到返回值。如果返回值不为null,则创建包含mapping返回值的Optional作为map方法返回值,否则返回空Optional。

map方法用来对Optional实例的值执行一系列操作。通过一组实现了Function接口的lambda表达式传入操作。


eg:

    //map方法执行传入的lambda表达式参数对Optional实例的值进行修改。
    //为Lambda表达式的返回值创建新的Optional实例作为map方法的返回值。
    Optional<String> upperName = myValue.map((value) -> value.toUpperCase());
    System.out.println(upperName.orElse("No value found"));

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


如果有值,为其执行mapping函数返回Optional类型返回值,否则返回空Optional。flatMap与map(Funtion)方法类似,区别在于flatMap中的mapper返回值必须是Optional。调用结束时,flatMap不会对结果用Optional封装。


flatMap方法与map方法类似,区别在于mapping函数的返回值不同。map方法的mapping函数返回值可以是任何类型T,而flatMap方法的mapping函数必须是Optional。


eg:


下面参照map函数,使用flatMap重写的示例


    //map方法中的lambda表达式返回值可以是任意类型,在map函数返回之前会包装为Optional。 
    //但flatMap方法中的lambda表达式返回值必须是Optionl实例。 
    upperName = myValue.flatMap((value) -> Optional.of(value.toUpperCase()));
    System.out.println(upperName.orElse("No value found"));

11) filter(Predicate<? super T> predicate)


如果有值并且满足断言条件返回包含该值的Optional,否则返回空Optional。



filter个方法通过传入限定条件对Optional实例的值进行过滤。


对于filter函数我们可以传入实现了Predicate接口的lambda表达式。


eg:

  //filter方法检查给定的Option值是否满足某些条件。
    //如果满足则返回同一个Option实例,否则返回空Optional。
    Optional<String> longName = myValue.filter((value) -> value.length() > 6);
    System.out.println(longName.orElse("The name is less than 6 characters"));//输出OptionalTest
    //另一个例子是Optional值不满足filter指定的条件。
    Optional<String> anotherName = Optional.of("Test");
    Optional<String> shortName = anotherName.filter((value) -> value.length() > 6);
    //输出:name长度不足6字符
    System.out.println(shortName.orElse("The name is less than 6 characters"));

12) empty()


返回一个空Optional实例。

eg:

Optional.<String>empty(); // 返回一个空Optional<String>

Note:


Optional有这么多方法,那Optional的初衷是什么?而且Optional也是一个对象,所以它本身也有可能是null,这可如何是好。所以,个人认为,Optional比较适用的地方是作为返回值,这样可以给使用者一个善意的提醒。




4.4  map 与 flatMap 的区别


map(mapper) 与 flatMap(mapper) 功能上基本是一样的,只是最后的返回值不一样。map(mapper)方法中的mapper可以返回任意类型,但是flatMap(mapper)方法中的mapper只能返回Optional类型。


如果mapper返回结果result的不是null,那么map就会返回一个Optional,但是 flatMap不会对result进行任何包装。


eg:

Optional<String> o;
o.map((value)->Optional.of(value)) //返回的类型是Optional<Optional<String>>
o.flatMap((value)->Optional.of(value)) //返回的类型是Optional<String>

4.5 Optional应用示例:


Note:测试的时候最好是新建一个java项目(或者直接用eclipse测试),因为Android项目目前不完全支持java8的新特性,需要配置很多东西,也一样容易出现各种问题。

import java.util.NoSuchElementException;
import java.util.Optional;
public class OptionalTest {
  public static void main(String[] args) {
     // 创建Optional
        String mayBeNull = null;
        Optional<String> opt1 = Optional.of(" Hello! ");
        Optional<String> opt2 = Optional.ofNullable(mayBeNull);
        Optional<String> opt3 = Optional.empty();
        opt1.ifPresent(System.out::println); // " Hello! "
        opt2.ifPresent(System.out::println);
        opt3.ifPresent(System.out::println);
        //方法测试示例
        ofTest();
        ofNullableTest();
        isPresentTest();
        ifPresentTest();
        orElseTest();
        orElseGetTest();
        mapTest();
        flatMapTest();
        filterTest();
  }
  /**
     * of后面接给optional设置的值 但是不能为空 如果为空会报空指针异常
     */
    public static void ofTest() {
        Optional<String> optional = Optional.of("123");
        System.out.println(optional.get());
        try {
            optional = Optional.of(null);
            System.out.println("null值--"+optional.get());  //get方法是获取optional的值 类型取决于声明的时候
        } catch (NullPointerException e) {
            System.out.println("空指针异常");
        }
    }
    /**
     * ofNullable 和of类似 但是ofNullable可以设置null值  如果是Null值得话取值会报NoSuchElementException 异常
     */
    public static void ofNullableTest() {
        Optional<String> optional = Optional.ofNullable("123");
        System.out.println(optional.get());
        try {
            optional = Optional.ofNullable(null);
            System.out.println("null值---"+optional.get());
        } catch (NoSuchElementException e) {
            System.out.println("NoSuchElementException 异常");
        }
    }
    /**
     * ifPresent用来判断optional中有没有值存在 如果有则为真
     */
    public static void isPresentTest() {
        Optional<String> optional = Optional.ofNullable(null);
        if (optional.isPresent()) {
            System.out.println(optional.get());
        } else {
            System.out.println("值为空");
        }
    }
    /**
     * ifPresent和isPresent类似 只不过它支持λ表达式
     */
    public static void ifPresentTest() {
        Optional<String> optional = Optional.ofNullable("123");
        optional.ifPresent(var -> {
            System.out.println(var);
        });
    }
    /**
     * orElse方法,如果值为空的话会用参数中的值去替换 即设置默认值
     */
    public static void orElseTest() {
        Optional<String> optional = Optional.ofNullable("123");
        System.out.println(optional.orElse("有没有"));
        optional = Optional.ofNullable(null);
        System.out.println(optional.orElse("有没有000"));
    }
    /**
     * orElseGet方法 和orElse类似 不过此方法接受Supplier接口的实现用来生成默认值
     */
    public static void orElseGetTest() {
        Optional<String> optional = Optional.ofNullable("123");
        System.out.println(optional.orElseGet(() -> "123456"));
        optional = Optional.ofNullable(null);
        System.out.println(optional.orElseGet(() -> "1234567"));
    }
    /**
     * map方法  如果有值则会对值进行mapping中的处理 处理结果存在则创建并返回Optional类型的结果 否则返回空
     */
    public static void mapTest() {
        Optional<String> optional = Optional.ofNullable("abc");
        System.out.println(optional.map(var -> var.toUpperCase()).get());
    }
    /**
     * flatMap和map类似 只不过mapping中必须返回Option类型的数据
     */
    public static void flatMapTest() {
        Optional<String> optional = Optional.ofNullable("abc");
        System.out.println(optional.flatMap(var -> Optional.of(var.toUpperCase())).get());
    }
    /**
     * filter对optional进行过滤,mapping中为过滤的条件  如果不满足条件 返回一个为空的Optional
     */
    public static void filterTest() {
        try {
            Optional<String> optional = Optional.ofNullable("一二三四五六七八");
            System.out.println(optional.filter(var -> var.length() > 6).get());
            System.out.println(optional.filter(var -> var.length() < 6).get());
        } catch (NoSuchElementException e) {
            System.out.println("optional的值为空");
        }
    }
}


目录
相关文章
|
26天前
|
存储 缓存 安全
java 中操作字符串都有哪些类,它们之间有什么区别
Java中操作字符串的类主要有String、StringBuilder和StringBuffer。String是不可变的,每次操作都会生成新对象;StringBuilder和StringBuffer都是可变的,但StringBuilder是非线程安全的,而StringBuffer是线程安全的,因此性能略低。
44 8
|
26天前
|
Java 开发者
在 Java 中,一个类可以实现多个接口吗?
这是 Java 面向对象编程的一个重要特性,它提供了极大的灵活性和扩展性。
59 1
|
1月前
|
Java Android开发
Eclipse 创建 Java 类
Eclipse 创建 Java 类
26 0
|
4月前
|
Java 开发者
奇迹时刻!探索 Java 多线程的奇幻之旅:Thread 类和 Runnable 接口的惊人对决
【8月更文挑战第13天】Java的多线程特性能显著提升程序性能与响应性。本文通过示例代码详细解析了两种核心实现方式:Thread类与Runnable接口。Thread类适用于简单场景,直接定义线程行为;Runnable接口则更适合复杂的项目结构,尤其在需要继承其他类时,能保持代码的清晰与模块化。理解两者差异有助于开发者在实际应用中做出合理选择,构建高效稳定的多线程程序。
62 7
|
2月前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
27 3
|
2月前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
38 2
|
2月前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
44 1
|
4月前
|
Oracle 安全 Java
JDK8到JDK28版本升级的新特性问题之在Java 15及以后的版本中,密封类和密封接口是怎么工作的
JDK8到JDK28版本升级的新特性问题之在Java 15及以后的版本中,密封类和密封接口是怎么工作的
|
4月前
|
安全 Java
【Java集合类面试三】、Map接口有哪些实现类?
这篇文章介绍了Java中Map接口的几种常用实现类:HashMap、LinkedHashMap、TreeMap和ConcurrentHashMap,以及它们适用的不同场景和线程安全性。
|
6月前
|
Java
Java中,有两种主要的方式来创建和管理线程:`Thread`类和`Runnable`接口。
【6月更文挑战第24天】Java创建线程有两种方式:`Thread`类和`Runnable`接口。`Thread`直接继承受限于单继承,适合简单情况;`Runnable`实现接口可多继承,利于资源共享和任务复用。推荐使用`Runnable`以提高灵活性。启动线程需调用`start()`,`Thread`直接启动,`Runnable`需通过`Thread`实例启动。根据项目需求选择适当方式。
64 2
下一篇
DataWorks