美文网首页
java8(新特性3)---Optional

java8(新特性3)---Optional

作者: Andy__Dufresne | 来源:发表于2020-03-24 15:10 被阅读0次

    It takes a strong man to save himself, and a great man to save another.

    一 概览

     Optional是java.util包中的一部分,因此为了使用Optional,需要:  import java.util.Optional
    

    二 创建

    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创建

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

    然而,传递给of()的值不可以为空,否则会抛出空指针异常,如下:

    @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()就不需要了。

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

    转载自:https://www.cnblogs.com/dzy521/p/10077132.html

    相关文章

      网友评论

          本文标题:java8(新特性3)---Optional

          本文链接:https://www.haomeiwen.com/subject/afmoyhtx.html