java8新特性之Optional类

摘要:
Optional不是对null关键字的一种代替,而是对于null推断提供了一种越来越高贵的落实

NullPointException能够说是持有java程序员都遇到过的叁个不行,即便java从设计之初就着力让工程师脱离指针的鬼世界,可是指针确实是实际上存在的,而java设计者也只好是让指针在java语言中变得越发简便易行、易用,而不能完全的将其除去,所以才有了大家平时所看到的根本字null

原创小说,转载请申明出处:《Java底子连串-Optional》

Java8新特点种类

空指针异常是多个运行时特别,对于这一类极其,若无刚烈的管理政策,那么最棒实施在于让程序早点挂掉,然而不菲情形下,不是开辟职员未有切实可行的拍卖政策,而是根本未有察觉到空指针格外的留存。当极度真的发生的时候,管理政策也很简单,在设有十分的地点增添三个if语句判别就可以,可是如此的答问攻略会让大家的顺序现身越来越多的null剖断,大家了解二个好好的前后相继设计,应该让代码中尽量少现身null关键字,而java8所提供的Optional类则在收缩NullPointException的同一时候,也升高了代码的雅观度。但第一我们须要鲜明的是,它并 不是对null非常重要字的一种代替,而是对于null剖断提供了一种更高贵的兑现,进而制止NullPointException

Optional的引进是为着解决null的难题,那么到底是以绝后患null的怎么难题吧?

  • Java8新特性(一) –
    lambda表达式
  • Java8新特性(二) – Optional类
  • Java8新特征(三) –
    流式数据处理
  • Java8新特色(四) –
    暗中认可接口方法
  • 待定

一. 直观后体会

一旦我们须求回到二个字符串的长短,假如不依靠第三方工具类,我们须要调用str.length()方法:

if(null == str) { // 空指针判定
    return 0;
}
return str.length();

倘若选择Optional类,实现如下:

return Optional.ofNullable(str).map(String::length).orElse(0);

Optional的代码相对更为简明,现代码量非常的大时,大家超轻松忘记举行null判断,然而接收Optional类则会制止那类难题。

我们掌握当大家本着null调用方法的之后,就能抛出空指针万分,Optional正是为着缓和那么些主题素材而来的。

NullPointException能够说是有着java程序员都遭受过的一个不胜,尽管java从设计之初就大力让程序猿脱离指针的苦海,可是指针确实是事实上存在的,而java设计者也只好是让指针在java语言中变得越来越简便易行、易用,而不能够一心的将其除去,所以才有了大家平常所看到的根本字null

二. 基本使用

Optional通过包装目的对象的艺术来表示,当大家应用的时候,Optional是无庸置疑存在的,因为假若结果为null,会回来叁个固定的EMPTY实例,那样就不会存在null引用的标题了。

空指针非常是一个运作时特别,对于这一类非常,若无显明的处理政策,那么最好实践在于让程序早点挂掉,但是洋洋境况下,不是开辟人士没有具体的拍卖政策,而是根本未曾意识到空指针相当的留存。当非常真的爆发的时候,管理政策也很简短,在设有相当的地点加多叁个if语句判断就可以,不过如此的答问战术会让大家的顺序现身愈来愈多的null判断,大家精晓二个两全其美的前后相继设计,应该让代码中尽量少现身null关键字,而java8所提供的Optional类则在缩小NullPointException的同一时候,也提高了代码的美观度。但第一我们要求掌握的是,它并 不是对null重大字的一种代替,而是对于null判断提供了一种更高尚的贯彻,进而防止NullPointException

1.对象成立

创建空对象

Optional<String> optStr = Optional.empty();

地方的上行下效代码调用empty()措施创立了三个空的Optional<String>对象型。

创立对象:不许为空
Optional提供了艺术of()用来创设非空对象,该措施供给传入的参数不可能为空,不然抛NullPointException,示举个例子下:

Optional<String> optStr = Optional.of(str);  // 当str为null的时候,将抛出NullPointException

创立对象:允许为空
假如无法明显传入的参数是不是留存null值的大概,则足以用Optional的ofNullable()艺术创制对象,假若入参为null,则开创五个空对象。示比方下:

Optional<String> optStr = Optional.ofNullable(str);  // 如果str是null,则创建一个空对象

那该如何来行使Optional呢?

一. 直观后体会

要是大家要求回到二个字符串的尺寸,假使不依附于第三方工具类,我们要求调用str.length()方法:

if(null == str) { // 空指针判定
    return 0;
}
return str.length();

假诺选拔Optional类,达成如下:

return Optional.ofNullable(str).map(String::length).orElse(0);

Optional的代码相对越发简洁,今世码量极大时,我们相当的轻便忘记实行null推断,不过利用Optional类则会制止那类难点。

2.流式管理

流式管理也是java8给大家带来的三个分量级新特点,让我们对聚焦的操作变得越来越精简和火速,下一篇关于java8新特色的随笔,将对未有管理进行周全的讲课。这里Optional也提供了三个为主的灭绝管理:映射和过滤。

为了演示,大家规划了二个User类,如下:

/**
 * @author: zhenchao.Wang 2016-9-24 15:36:56
 */
public class User {

    /** 用户编号 */
    private long id;

    private String name;

    private int age;

    private Optional<Long> phone;

    private Optional<String> email;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 省略setter和getter
}

手提式有线电话机和信箱不是一位的必得有的,所以大家应用Optional定义。

映射:map与flatMap
辉映是将输入调换到其余一种样式的输出的操作,比如前面例子中,大家输入字符串,而输出的是字符串的长短,那就是一种隐射,我们采取情势map()可以完成。若是大家盼望获得壹个人的全名,那么我们能够如下完成:

String name = Optional.ofNullable(user).map(User::getName).orElse("no name")

像这种类型当入参user不为空的时候则赶回其name,不然重回no name 如作者大家期望通过上边方式获得phone或email,利用方面包车型客车法子则不算了,因为map之后回到的是Optional,大家把这种称为Optional嵌套,大家必得在map叁次才干取得大家想要的结果:

long phone = optUser.map(User::getPhone).map(Optional::get).orElse(-1L);

实则那个时候,更加好的艺术是运用flatMap,一步取得我们想要的结果:

long phone = optUser.flatMap(User::getPhone).orElse(-1L);

flapMap能够将艺术重返的逐一流扁平化成为叁个流,具体在下一篇极度讲流式处理的随笔中细说。

过滤:fliter
filiter,看名就能猜到其意义是过滤的操作,大家得以将过滤操作做为参数字传送递给该措施,进而实现过滤目标,参加大家盼望筛选18周岁上述的大人,则足以达成如下:

optUser.filter(u -> u.getAge() >= 18).ifPresent(u -> System.out.println("Adult:" + u));

大家不能够滥用Optional,只有在逻辑上恐怕为null时才使用Optional来封装目的对象,假如逻辑上一定不恐怕为null时,就不用选取Optional封装对象,那样当阅读代码的人看来有Optional则表示目的对象是可认为null的。

二. 基本接纳

3.默许行为

暗中认可行为是当Optional为不满意条件时所实施的操作,比如在地点的事例中我们接收的orElse()正是三个私下认可操作,用于在Optional对象为空时实施一定操作,当然也会有局地暗许操作是当满足条件的对象存在时实践的操作。

get()
get用于获取变量的值,不过当变量不设不时则会抛出NoSuchElementException,所以如若不明确变量是还是不是留存,则不建议利用

orElse(T other)
当Optional的变量不满意给定条件时,则实行orElse,比如前边当str为null时,重回0。

orElseGet(Supplier<? extends X> expectionSupplier)
倘若条件不树立刻,须要执行相对复杂的逻辑,并非总结的归来操作,则足以选拔orElseGet完成:

long phone = optUser.map(User::getPhone).map(Optional::get).orElseGet(() -> {
    // do something here
    return -1L;
});

orElseThrow(Supplier<? extends X> expectionSupplier)
与get(卡塔尔方法雷同,都以在不满意条件时再次来到万分,不过这里大家得以钦点再次回到的不行类型。

ifPresent(Consumer<? super T>)
当满足条件时举行传入的参数化操作。

与此相类似现在,如若程序中又冒出了null指针十分,那么只好是你的代码逻辑有误,而不容许是非逻辑原因了,也等于不应该为null的时候现身了null,那分明是你逻辑搞错了。

1.目标创造

始建空对象

Optional<String> optStr = Optional.empty();

地点的演示代码调用empty()方式创立了叁个空的Optional<String>对象型。

创制对象:不容许为空
Optional提供了法子of()用以创立非空对象,该方法须求传入的参数不能够为空,否则抛NullPointException,示比方下:

Optional<String> optStr = Optional.of(str);  // 当str为null的时候,将抛出NullPointException

创设对象:允许为空
假设不可能鲜明传入的参数是不是存在null值的恐怕性,则足以用Optional的ofNullable()情势创立对象,假诺入参为null,则开创一个空对象。示比如下:

Optional<String> optStr = Optional.ofNullable(str);  // 如果str是null,则创建一个空对象

三. 注意事项

Optional是贰个final类,未达成其余接口,所以当大家在接收该类包装定义类的属性的时候,固然大家定义的类有种类化的须要,那么因为Optional未有兑现Serializable接口,当时实行种类化操作就能够非凡:

public class User implements Serializable{

    /** 用户编号 */
    private long id;

    private String name;

    private int age;

    private Optional<Long> phone;  // 不能序列化

    private Optional<String> email;  // 不能序列化

只是大家可以动用如下替换战术:

private long phone;

public Optional<Long> getPhone() {
    return Optional.ofNullable(this.phone);
}

 

2.1 创建Optional实例

public class OptionalTest { public static void main(String[] args) { Optional<String> o = Optional.empty();// 创建一个空Optional }}

public class OptionalTest { public static void main(String[] args) { Optional<String> op = Optional.of;// 创建一个目标对象必须有值的Optional }}

这种创立方式,倘若of的参数为null,则直接抛出特别。

public class OptionalTest { public static void main(String[] args) { Optional<String> opt = Optional.ofNullable;// 创建一个目标对象可为null的Optional }}

2.流式处理

流式处理也是java8给大家带给的二个重量级新特点,让我们对集中的操作变得尤其简洁和飞速,下一篇有关java8新特征的篇章,将对未有管理進展完善的任课。这里Optional也提供了两个中央的收敛管理:映射和过滤。

为了演示,大家统筹了三个User类,如下:

/**
 * @author: zhenchao.Wang 2016-9-24 15:36:56
 */
public class User {

    /** 用户编号 */
    private long id;

    private String name;

    private int age;

    private Optional<Long> phone;

    private Optional<String> email;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 省略setter和getter
}

手提式有线电话机和邮箱不是一人的总得有的,所以大家利用Optional定义。

映射:map与flatMap
照耀是将输入转变到其余一种样式的出口的操作,举个例子后面例子中,我们输入字符串,而输出的是字符串的长度,那便是一种隐射,大家运用方式map()能够兑现。假若大家盼望获得一人的真名,那么大家得以如下落成:

String name = Optional.ofNullable(user).map(User::getName).orElse("no name");

那般当入参user不为空的时候则赶回其name,不然重回no name 如小编我们愿意经过上边方式获取phone或email,利用方面包车型客车主意则不行了,因为map之后再次来到的是Optional,我们把这种称为Optional嵌套,大家必得在map一次能力获得大家想要的结果:

long phone = optUser.map(User::getPhone).map(Optional::get).orElse(-1L);

实在这里个时候,更加好的法子是行使flatMap,一步获得我们想要的结果:

long phone = optUser.flatMap(User::getPhone).orElse(-1L);

flapMap能够将艺术再次回到的逐个流扁平化成为二个流,具体在下一篇非常讲流式管理的篇章中细说。

过滤:fliter
filiter,看名称就会想到其意义是过滤的操作,大家得以将过滤操作做为参数字传送递给该措施,进而完结过滤目标,参与大家盼望筛选18周岁上述的中年人,则能够完结如下:

optUser.filter(u -> u.getAge() >= 18).ifPresent(u -> System.out.println("Adult:" + u));

2.2 中间操作-管理Optional

本条filter是一个校验器,由于Optional中也只有二个要素,称其为过滤有一点点太大了,若是Optional中的成分知足给定的校验条件,则将封装成分的Optional重回,不然重临空的Optional。

public class OptionalTest { public static void main(String[] args) { filterTest(); } public static void filterTest(){ Optional<String> os = Optional.of.filter(e -> e.length; System.out.println(os.isPresent; }}

施行结果:

false

因为字符串“123456”长度小于7,所以回来三个空的Optional。

map是映射之意,就是针对Optional封装的要素实行操作,然后回到叁个装进着操作结果的新的Optional。

public class OptionalTest { public static void main(String[] args) { mapTest(); } public static void mapTest(){ Optional<Integer> oi = Optional.of("abcdefg").map(e -> e.length; System.out.println; }}

举行理并了结果:

7

以此办法是扁平化映射,所谓扁平正是管理嵌套的Optional。

诸如有三个Person类,此中有一个Optional<String>类型的字段name,大家在别处获取到了一个Optional<Person>类型的实例,现在想要获取到这一个Person的的真名,那正是嵌套的Optional。

class Person{ Optional<String> name; public Person(String name){ this.name = Optional.of; } public Optional<String> getName(){ return name; }}

举例我们运用map获取到的是之类的:

public class OptionalTest { public static void main(String[] args) { flatMapTest(); } public static void flatMapTest(){ Optional<Person> op = Optional.of(new Person); Optional<Optional<String>> oos = op.map(Person::getName); String name = oos.get().orElseGet->"noName"); System.out.println; }}

而笔者辈应用flatMap的话就足以是这么的:

public class OptionalTest { public static void main(String[] args) { flatMapTest(); } public static void flatMapTest(){ Optional<Person> op = Optional.of(new Person); Optional<String> os = op.flatMap(Person::getName); String name = os.orElseGet->"noName"); System.out.println; }}

嵌套的Optional要获取到指标对象,必需通过一再去壳才行,那也是应用map映射的准则,不过利用flatMap,扁平化功用,一回去壳操作就完结了。

3.默认行为

暗中认可行为是当Optional为不满意条件时所实践的操作,举个例子在地点的事例中我们接纳的orElse()正是多个默许操作,用于在Optional对象为空时施行一定操作,当然也可能有部分默许操作是当满意条件的对象存在时推行的操作。

get()
get用于获取变量的值,不过当变量不设有时则会抛出NoSuchElementException,所以假设不明确变量是或不是存在,则不提出利用

orElse(T other)
当Optional的变量不满意给定条件时,则奉行orElse,比方前边当str为null时,重返0。

orElseGet(Supplier<? extends X> expectionSupplier)
假使条件不树立即,要求实行相对复杂的逻辑,并不是粗略的归来操作,则足以接受orElseGet达成:

long phone = optUser.map(User::getPhone).map(Optional::get).orElseGet(() -> {
    // do something here
    return -1L;
});

orElseThrow(Supplier<? extends X> expectionSupplier)
与get(卡塔尔方法近似,都以在不满意条件时再次回到格外,可是这里我们能够钦命重临的不胜类型。

ifPresent(Consumer<? super T>)
当满意条件时进行传入的参数化操作。

2.3 从Optional中赢得目的值

有三种方法:

  • public T
    get(卡塔尔:直接拿走Optional中的值,若是是空的要抛出NoSuchElementException格外
  • public T orElse:若是Optional不为空,则将值再次来到,不然重临钦命的other
  • public T orElseGet(Supplier<? extends T>
    other卡塔尔(قطر‎:如若Optional不为空,则重回值,不然将通过点名格局other生成的值重临
  • public <X extends Throwable> T orElseThrow(Supplier<?
    extends X> exceptionSupplier卡塔尔 throws
    X:假诺Optional不为空,重回值,不然抛出由钦点的exceptionSupplier生成的万分

public class OptionalTest { public static void main(String[] args) { getTest(); orElseTest(); } public static void getTest(){ Optional<String> os = Optional.of; System.out.println; } public static void orElseTest(){ Optional<String> os = Optional.empty(); System.out.println(os.orElse("default")); System.out.println(os.orElseGet->"default")); System.out.println(os.orElseThrow(RuntimeException::new)); }}

施行结果:

123456defaultdefaultException in thread "main" java.lang.RuntimeException at java.util.Optional.orElseThrow(Optional.java:290) at com.dh.stream.OptionalTest.orElseTest(OptionalTest.java:29) at com.dh.stream.OptionalTest.main(OptionalTest.java:17)

Optional使用时无法直接get,那个时候会走进老路,get的时候大概为null,就能够抛出特别,那时你就能想要在get早先开展isPresent判别,这一个不行使Optional又有什么分别的。

笔者们利用Optional正是为了简化null决断,所以拒却使用get方法,Optional提供了金科玉律行使的方法是:orElse、orElseGet、orElseThrow四个方式。

行使orElse方法,大家得以从非空的Optional中收获到值,假诺是空的Optional,能够回到orElse方法参数钦命的私下认可值。

利用orElseGet方法,我们可以在空的Optional的情事下主动创设多少个默许重返结果。

orElseThrow方法,在空Optional的景况下会抛出贰个钦点的老大。

说了那般多,最后是为着在支付中运用Optional,正如开端时说的,我们要旗帜明显Optional躲藏的是这种null,无法在具备的地点都利用它。

当项指标事体法则下某些对象大概为null(正是工作允许的null),这种场馆下,现身null是正规现象,这种气象供给规避,大家选择Optional封装目的对象,保障不会设有null调用抛出空指针。

只是如若在业务法则下某些对象不容许为null(就是专门的工作不容许为null),这种情状下,现身null便是程序出错了,并非例行处境,这种时候大家不可能用Optional去封装指标对象来规避难点,而是要平素动用,一旦出错就足以即时的各种审核难题,不至于被Optional将标题给隐讳掉。

三. 注意事项

Optional是一个final类,未落到实处别的接口,所以当我们在行使该类包装定义类的习性的时候,假若大家定义的类有种类化的须求,那么因为Optional未有落到实处塞里alizable接口,那时施行体系化操作就会有标题:

public class User implements Serializable{

    /** 用户编号 */
    private long id;

    private String name;

    private int age;

    private Optional<Long> phone;  // 不能序列化

    private Optional<String> email;  // 不能序列化

然则大家得以接受如下替换战术:

private long phone;

public Optional<Long> getPhone() {
    return Optional.ofNullable(this.phone);
}

如上所述Optional在布置的时候就不曾构思将它充任类的字段使用~

You can leave a response, or trackback from your own site.

Leave a Reply

网站地图xml地图