Java8之Optional用法

原文地址:https://www.baeldung.com/java-optional  只是将其翻译一遍,加上些本身的理解,顺便总结一下java

一 概览web

     Optional是java.util包中的一部分,所以为了使用Optional,须要:oracle

import java.util.Optional;

 

二 建立app

2.1 调用empty API, 建立一个空的Optional对象函数

@Test
public void whenCreatesEmptyOptional_thenCorrect() {
    Optional<String> empty = Optional.empty();
    assertFalse(empty.isPresent());
}

Ps: isPresent API 是用来检查Optional对象中是否有值。只有当咱们建立了一个含有非空值的Optional时才返回true。在下一部分咱们将介绍这个API。测试

 

2.2 使用staticAPI建立this

@Test
public void givenNonNull_whenCreatesOptional_thenCorrect() {
    String name = "baeldung";
    Optional<String> opt = Optional.of(name);
    assertEquals("Optional[baeldung]", opt.toString());
}

然而,传递给of()的值不能够为空,不然会抛出空指针异常,以下:spa

@Test(expected = NullPointerException.class)
public void givenNull_whenThrowsErrorOnCreate_thenCorrect() {
    String name = null;
    Optional<String> opt = Optional.of(name);
}

有时咱们须要传递一些空值,那咱们可使用下面这个API:翻译

@Test
public void givenNonNull_whenCreatesNullable_thenCorrect() {
    String name = "baeldung";
    Optional<String> opt = Optional.ofNullable(name);
    assertEquals("Optional[baeldung]", opt.toString());
}

使用ofNullable API,则当传递进去一个空值时,不会抛出异常,而只是返回一个空的Optional对象,如同咱们用Optional.empty API:指针

@Test
public void givenNull_whenCreatesNullable_thenCorrect() {
    String name = null;
    Optional<String> opt = Optional.ofNullable(name);
    assertEquals("Optional.empty", opt.toString());
}

 

三  使用isPresent API 检查值

咱们可使用这个API检查一个Optional对象中是否有值,只有值非空才返回true

@Test
public void givenOptional_whenIsPresentWorks_thenCorrect() {
    Optional<String> opt = Optional.of("Baeldung");
    assertTrue(opt.isPresent());
 
    opt = Optional.ofNullable(null);
    assertFalse(opt.isPresent());
}

 

四 适当状况下使用isPresent API 

传统上,咱们通常这样写来检查空值:

if(name != null){
    System.out.println(name.length);
}

问题在于,有时候咱们会忘记了对空值进行检查,这时就可使用这个API:

@Test
public void givenOptional_whenIfPresentWorks_thenCorrect() {
    Optional<String> opt = Optional.of("baeldung");
 
    opt.ifPresent(name -> System.out.println(name.length()));
}

 

五 orEse && orElseGet

5.1   orElse

这个API被用来检索Optional对象中的值,它被传入一个“默认参数‘。若是对象中存在一个值,则返回它,不然返回传入的“默认参数”,以下所示:

@Test
public void whenOrElseWorks_thenCorrect() {
    String nullName = null;
    String name = Optional.ofNullable(nullName).orElse("john");
    assertEquals("john", name);
}

 

5.2 orElseGet

与orElsel相似,可是这个函数不接收一个“默认参数”,而是一个函数接口,以下例所示:

@Test
public void whenOrElseGetWorks_thenCorrect() {
    String nullName = null;
    String name = Optional.ofNullable(nullName).orElseGet(() -> "john");
    assertEquals("john", name);
}

 

5.3 二者区别

 要想理解这两者,首先让咱们建立一个无参且返回定值的方法:

public String getMyDefault() {
    System.out.println("Getting Default Value");
    return "Default Value";
}

接下来,进行两个测试看看它们有什么区别:

@Test
public void whenOrElseGetAndOrElseOverlap_thenCorrect() {
    String text;
 
    System.out.println("Using orElseGet:");
    String defaultText = 
      Optional.ofNullable(text).orElseGet(this::getMyDefault);
    assertEquals("Default Value", defaultText);
 
    System.out.println("Using orElse:");
    defaultText = Optional.ofNullable(text).orElse(getMyDefault());
    assertEquals("Default Value", defaultText);
}

在这里示例中,咱们的Optional对象中包含的都是一个空值,让咱们看看程序执行结果:

Using orElseGet:
Getting default value...
Using orElse:
Getting default value...

两个Optional对象中都不存在value,所以执行结果相同。

 

那么当Optional对象中值存在时又是怎样呢?

@Test
public void whenOrElseGetAndOrElseDiffer_thenCorrect() {
    String text = "Text present";
 
    System.out.println("Using orElseGet:");
    String defaultText 
      = Optional.ofNullable(text).orElseGet(this::getMyDefault);
    assertEquals("Text present", defaultText);
 
    System.out.println("Using orElse:");
    defaultText = Optional.ofNullable(text).orElse(getMyDefault());
    assertEquals("Text present", defaultText);
}

让咱们看看执行结果:

Using orElseGet:
Using orElse:
Getting default value...

能够看到,当使用orElseGet去检索值时,getMyDefault并不执行,由于Optional中含有值,而使用orElse时则照常执行。因此能够看到,当值存在时,orElse相比于orElseGet,多建立了一个对象,可能从这个实例中你感觉不到影响有多大,但考虑当getDefalut不只仅是个简单函数,而是一个web service之类的,则多建立一个代价是比较大的。

 

六   orElseThrow

orElseThrow当遇到一个不存在的值的时候,并不返回一个默认值,而是抛出异常,以下所示:

@Test(expected = IllegalArgumentException.class)
public void whenOrElseThrowWorks_thenCorrect() {
    String nullName = null;
    String name = Optional.ofNullable(nullName).orElseThrow(
      IllegalArgumentException::new);
}

 

七  使用get()

@Test
public void givenOptional_whenGetsValue_thenCorrect() {
    Optional<String> opt = Optional.of("baeldung");
    String name = opt.get();
 
    assertEquals("baeldung", name);
}

使用get() API 也能够返回被包裹着的值。可是必须是值存在时,当值不存在时,它会抛出一个NoSuchElementException异常,以下所示:

@Test(expected = NoSuchElementException.class)
public void givenOptionalWithNull_whenGetThrowsException_thenCorrect() {
    Optional<String> opt = Optional.ofNullable(null);
    String name = opt.get();
}

由于这个方法与咱们使用Optional的目的相违背,因此能够预见在不久未来它或许会被抛弃,建议仍是使用其余的方法。

 

八   filter()

接收一个函数式接口,当符合接口时,则返回一个Optional对象,不然返回一个空的Optional对象。

@Test
public void whenOptionalFilterWorks_thenCorrect() {
    Integer year = 2016;
    Optional<Integer> yearOptional = Optional.of(year);
    boolean is2016 = yearOptional.filter(y -> y == 2016).isPresent();
    assertTrue(is2016);
    boolean is2017 = yearOptional.filter(y -> y == 2017).isPresent();
    assertFalse(is2017);
}

这个API做用通常就是拒绝掉不符合条件的,好比拒绝掉错误的电子邮箱。

让咱们看下一个更有意义的例子,假如咱们想买一个调制解调器,而且只关心它的价格:

public class Modem {
    private Double price;
 
    public Modem(Double price) {
        this.price = price;
    }
    //standard getters and setters
}

接下来,咱们想要检查每一类调制解调器是否在咱们能够承受的价格范围内,那咱们在不使用Optional时该如何作呢?

public boolean priceIsInRange1(Modem modem) {
    boolean isInRange = false;
 
    if (modem != null && modem.getPrice() != null
      && (modem.getPrice() >= 10
        && modem.getPrice() <= 15)) {
 
        isInRange = true;
    }
    return isInRange;
}

咱们居然要写这么多code,尤为是if条件语句,然而对于这部分code最关键的实际上是对价格范围的判断。

这是一个Test例子:

@Test
public void whenFiltersWithoutOptional_thenCorrect() {
    assertTrue(priceIsInRange1(new Modem(10.0)));
    assertFalse(priceIsInRange1(new Modem(9.9)));
    assertFalse(priceIsInRange1(new Modem(null)));
    assertFalse(priceIsInRange1(new Modem(15.5)));
    assertFalse(priceIsInRange1(null));
}

若是长时间不用,那么有可能会忘记对null进行检查,那么若是使用Optional,会怎么样呢?

public boolean priceIsInRange2(Modem modem2) {
     return Optional.ofNullable(modem2)
       .map(Modem::getPrice)
       .filter(p -> p >= 10)
       .filter(p -> p <= 15)
       .isPresent();
 }

map()仅仅是将一个值转换为另外一个值,请谨记在心,这个操做并不会改变原来的值

让咱们仔细看看这段代码,首先,当咱们传入一个null时,不会发生任何问题。其次,咱们在这段code所写的惟一逻辑就如同此方法名所描述。

以前的那段code为了其固有的脆弱性,必须作更多,而如今不用了。

 

九   map()

在以前的例子中,咱们使用filter()来接受/拒绝一个一个值,而使用map()咱们能够将一个值转换为另外一个值

@Test
public void givenOptional_whenMapWorks_thenCorrect() {
    List<String> companyNames = Arrays.asList(
      "paypal", "oracle", "", "microsoft", "", "apple");
    Optional<List<String>> listOptional = Optional.of(companyNames);
 
    int size = listOptional
      .map(List::size)
      .orElse(0);
    assertEquals(6, size);
}

在这个例子中,咱们使用一个List包含了一些字符串,而后再把这个List包裹起来,对其map(),咱们这里是对这个List求它的size。

map()返回的结果也被包裹在一个Optional对象中,咱们必须调用合适的方法来查看其中的值。

注意到filter()仅仅是对值进行一个检查并返回一个boolean(很奇怪,照前面所述不该返回一个Optional对象吗?),而map()是使用现有的值进行计算,而且返回一个包裹着计算结果(映射结果)的Optional对象。

@Test
public void givenOptional_whenMapWorks_thenCorrect2() {
    String name = "baeldung";
    Optional<String> nameOptional = Optional.of(name);
 
    int len = nameOptional
     .map(String::length())
     .orElse(0);
    assertEquals(8, len);
}

将filter()与map()一块儿使用能够作一些很强力的事情。

假设咱们如今要检查一个用户的密码,那么咱们能够这样作:

@Test
public void givenOptional_whenMapWorksWithFilter_thenCorrect() {
    String password = " password ";
    Optional<String> passOpt = Optional.of(password);
    boolean correctPassword = passOpt.filter(
      pass -> pass.equals("password")).isPresent();
    assertFalse(correctPassword);
 
    correctPassword = passOpt
      .map(String::trim)
      .filter(pass -> pass.equals("password")) .isPresent();
    assertTrue(correctPassword);
}

注意到,若是不进行trim,则会返回false,这里咱们可使用map()进行trim。

 

 

十 flatmap()

有时咱们可使用flatmap()替换map(),两者不一样之处在于,map()只有当值不被包裹时才进行转换,而flatmap()接受一个被包裹着的值而且在转换以前对其解包

咱们如今有一个Person类:

public class Person {
    private String name;
    private int age;
    private String password;
 
    public Optional<String> getName() {
        return Optional.ofNullable(name);
    }
 
    public Optional<Integer> getAge() {
        return Optional.ofNullable(age);
    }
 
    public Optional<String> getPassword() {
        return Optional.ofNullable(password);
    }
    // normal constructors and setters
}

咱们能够像对待String同样将其包裹起来:

Person person = new Person("john", 26);
Optional<Person> personOptional = Optional.of(person);

注意当咱们包裹一个Person对象时,它将包含一个嵌套的Optional例子:

@Test
public void givenOptional_whenFlatMapWorks_thenCorrect2() {
    Person person = new Person("john", 26);
    Optional<Person> personOptional = Optional.of(person);
 
    Optional<Optional<String>> nameOptionalWrapper  
      = personOptional.map(Person::getName);
    Optional<String> nameOptional  
      = nameOptionalWrapper.orElseThrow(IllegalArgumentException::new);
    String name1 = nameOptional.orElse("");
    assertEquals("john", name1);
 
    String name = personOptional
      .flatMap(Person::getName)
      .orElse("");
    assertEquals("john", name);
}

须要注意,方法getName返回的是一个Optional对象,而不是像trim那样。这样就生成了一个嵌套的Optional对象。

所以使用map,咱们还须要再解包一次,而使用flatMap()就不须要了。

 

 

错误不当之处请指出,谢谢。

相关文章
相关标签/搜索