Java8学习笔记 -- Optional类

在开发过程中,空指针异常是最常见,不过也是比较容易修改的。尽管如此,为了避免空指针,我们可能会加入大量的检测逻辑。好在Java8中为我们提供了Optional类,它拥有一整套完善的为空检测及处理逻辑,大大的方便了我们的开发。

Optional类的Javadoc

Optional类实际上就是一个容器,里面保存着我们的对象,并提供取方法,并且可以为存为null的对象。

创建一个Optional对象:

1.Optional.of(obj)

这个方法需要传入一个非空的对象

2.Optional.ofNullable(obj)

这个不同于上面的方法,它允许传入一个为空的对象

3.Optional.empty()

严格意义来说,这个并不算,他是返回一个空的Optional实例,注意,并不是为null的实例,而是不包含其他对象的实例。源码如下:

private static final Optional<?> EMPTY = new Optional<>();

public static<T> Optional<T> empty() {
        @SuppressWarnings("unchecked")
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }

具体使用

获得了一个Optional实例后,关键就是怎么好好使用。首先这个类提供了一个为空判断方法:isPresent(),但是千万不要单纯的使用这个方法,比如:

private void test(Person person){
        Optional<Person> optional = Optional.ofNullable(person);
        if(optional.isPresent()){
            //...
            return;
        }
        //...
    }

那简直是对这个类的一大浪费,而且又无故多写了许多代码。我们可以看一下isPresent()的实现:

public boolean isPresent() {
        return value != null;
    }

这不就是我们经常写的为空判断么。而且大致看一眼这个类的方法,也能感觉到它不仅仅只是做一个判断这么简单。接下来,我们就一起来学习一下其他那些非常有用的方法,之后你也许就会感受到设计人员的良苦用心了。

1.get()

首先我们来看看取对象的方法,在以此为扩展了解其他方法。Optional既然是一个容器,那么肯定能存能取,不然要他干嘛?

这个方法语义很清晰,就是取之前我们保存的对象,当对象为空时,抛出空指针异常。可能有人要问了,既然只是简单的取值,而且还是会抛异常,哪有何用?对,这个方法本身并没啥用,就是一个简单的get方法,除非你配合isPresent(),但那样又回到了以前的思维,所以我们主要来看后面的方法。

2.orElse(T other)

这个就比较使用了,他要求传入一个默认值,当我们之前保存的值为空时,就返回传入的默认值,否则返回之前保存的值。

public static void main(String[] args){
        test(null); //10
        test(new Person(15)); //15
    }

    public static void test(Person person){
        Optional<Person> optional = Optional.ofNullable(person);
        out.println(optional.orElse(new Person(10)).getAge());
    }

帮我们省略了一些if-else逻辑,是不是很人性化?不要急着满足,后面还有更精彩的。

3.orElseGet(Supplier<? extends T> other)

这个与上面类似,只不过为空时不再返回一个默认值,而是通过提供的方法,返回这个方法所返回的值:

public static void main(String[] args){
        test(null); //10
        test(new Person(15)); //15
    }

public static void test(Person person){
        Optional<Person> optional = Optional.ofNullable(person);
        out.println(optional.orElseGet(()->new Person(10)).getAge());
    }

(为了简洁,我一般都用lambda表达式形式书写代码,如果对lambda表达式不熟悉的,可以参考我的这篇文章)

4.ifPresent(Consumer<? super T> consumer)

一般来说,我们进行为空判断后肯定要接着执行一些操作。所以,设计人员为我们提供了这个方法,基本功能就是如果我们所存的对象不为空时,执行所指定的代码,为空时什么都不做:

public static void test(Person person){
        Optional<Person> optional = Optional.ofNullable(person);
        optional.ifPresent(p -> out.println(p.getAge()));
    }

这样一来为空判断和后续逻辑就连贯在一起了。

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

有时候,我们从某个不为空的对象上使用某些方法后,也会生成空对象,因此就要要连续多级连续判断,放在以前,就要大量if-else嵌套,而map函数很方便这些操作:

optional.map(p -> p.getName())
        .map(n -> n.toUpperCase())
        .orElse("ABC")

换成等价的if-else代码

if (person != null) {
       String name = person.getName();
       if (name != null) {
            out.println(name.toUpperCase());
        }else{
            out.println("ABC");
        }
}else{
        out.println("ABC");
}

map和ifPresent类似,也是在不为空时执行指定逻辑,只不过map返回的也是一个Optional,所以map链可以无限延长,代替了if嵌套if的语句,不仅简单而且结构清晰。如果某一环为空,则不会执行所指定的逻辑,返回一个空的Optional实例。所以我们可以在某一环添加一些其他的方法,进行为空时的逻辑处理。类似的自由组合可以创造无限可能。

6.filter(Predicate<? super T> predicate)

了解过流式编程的朋友看到Optional的这些方法可能会很熟悉(如果不熟悉也没关系,在下一篇文章中学习过Stream类后,你就会很了解这类编程风格),这些方法应该是这一类API的标准方法,所以怎能少的了filter?

顾名思义,这个方法就是过滤的意思,只有符合我们所指定的逻辑后,才会向下传递,否者传递一个空的Optional,当然,身为空指针检测类,他的前提条件当然是不为空,否则不会执行传入的代码:

optional.filter(p -> p.getAge()>18).orElse(new Person(19, "jack")).getAge()

这个就很方便我们在不为空的同时进行逻辑判断,同时由于filter也是返回一个Optional对象,所以也可以搭配其他方法使用。

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

这个方法名字中也有一个map,那么应该可map有关联。对的,他的功能和map差不多,都是类似转换功能,但是,map的返回值是自动帮我们包装好的Optional对象,而flatmap这需要我们自己包装一个Optional对象:

optional.flatMap(p -> Optional.ofNullable(p.getName()))

可见flatMap灵活性较map大一点,不过map既然这样设计也是方便我们开发,不一定每次为空检测后,都要返回一个不相干的类型,所以如何取舍,还是自己决定。ψ(*`ー´)ψ

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

Optional类的方法并不多,到这里我们已经讲得差不多了,但是是不是觉得少点什么呢?原来前面的方法基本上都是检测为空时,直接不执行了,这样虽然不会报错,但是对于我们后期维护检测问题并不好。

Optional.of虽然会抛异常,但是他是在一开始包装的时候就抛,如果这个对象不为空,对象的成员为空呢?总不能还要用get()方法吧,get虽然可以,但只是抛一个传统的空指针异常,如果要抛自定义异常呢?

这时候就要用orElseThrow了,他在为空时会抛一个我们指定的异常,由于传递的是代码块,所以我们也可以在抛异常前,执行一些其他逻辑:

optional.orElseThrow(()->{out.println("hello");return new Throwable("abc");});

小结

这个类主要功能就是空指针检测,只不过设计人员为我们添加了许多实用的扩展方法,节省了我们大量代码,总体来说是很不错的。有兴趣的朋友可以看一看这个类是怎么实现的,由于是相对独立的,所以理解起来难度并不高。而且这个类的方法都是比较简单地,基础好的朋友也许自己都能创造出类似的类。

总之,既然为我们提供了这么实用的工具,我们就要在以后的实践中尽量有意识的多用用,不要再像以前一样if...else... ๑乛乛๑

Java8学习笔记目录

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,585评论 18 139
  • Java8 in action 没有共享的可变数据,将方法和函数即代码传递给其他方法的能力就是我们平常所说的函数式...
    铁牛很铁阅读 1,208评论 1 2
  • 喜欢电影《罗马假日》里面的一句话「身体或者心灵,总有一个在路上」,而导游似乎是亲身践行这句话的一个职业。 也许有人...
    水墨无痕1阅读 219评论 0 1
  • 哥哥虽在政法界工作,但也是位资深驴友,每年都会组织两三回说走就走的旅行。总想向哥哥学习,像他们一样洒脱,也...
    哈罗宝哥阅读 199评论 2 4
  • 一.默认事件-下面是一个a标签,默认具有跳转属性这就属于默认事件,怎么做到点击a标签让其不跳,去除默认事件。 二:...
    大胡子111阅读 838评论 0 3