Java8之Optional类,巧解NPE

NullPointerException——空指针异常是程序中常见异常之一,也是导致程序运行失败的常见异常。以前,为了防止出现null,我们常在代码中使用if…else…做防御性检查,后来Guava为了解决上述方法造成的代码污染引入了Optional类。

Java8借鉴Guava的Optional也加入了同名的Optional类,Optional类提供了很多实用的方法,借此可以避免显示的空指针判断,从而避免NullPointerException

常见方法

下面逐一讲解Optional类提供的方法。

of方法
public static <T> Optional<T> of(T value) {
    return new Optional<>(value);
}

Optional中的构造方法都是private的,外部无法使用new方式创建Optional对象,但Optional类提供三个方法用来获取Optional的实例对象。of方法是其中之一,用来创建一个包装对象值非空的Optional实例对象,若包装对象值为空则抛出NullPointerException异常。

// user若为null则会抛出NullPointerException异常
Optional<User> optional_user = Optional.of(user);
ofNullable方法
public static <T> Optional<T> ofNullable(T value) {
    return value == null ? empty() : of(value);
}

获取Optional实例对象的方法之一,返回一个允许包装对象值为空的Optional实例对象。ofNullableof方法的区别就在是否允许包装对象为空。

// 可以为空,且optional_user1 == optional_user2为true
Optional<User> optional_user1 = Optional.ofNullable(null);
Optional<User> optional_user2 = Optional.ofNullable(null);
empty方法
public static<T> Optional<T> empty() {
    @SuppressWarnings("unchecked")
    Optional<T> t = (Optional<T>) EMPTY;
    return t;
}

获取Optional实例对象的方法之一,创建一个包装对象值为空的Optional实例对象。

// 返回包装对象值为空的Optional实例对象
Optional<User> optional_user = Optional.empty();
isPresent方法
public boolean isPresent() {
    return value != null;
}

判断包装对象值是否为空。

// 这里的user不为null
Optional<User> optional_user1 = Optional.ofNullable(user);
Optional<User> optional_user2 = Optional.ofNullable(null);

// 返回true
optional_user1.isPresent();
// 返回false
optional_user2.isPresent();
ifPresent方法
public void ifPresent(Consumer<? super T> consumer) {
    if (value != null)
        consumer.accept(value);
}

判断包装对象值是否为空,如果包装对象的值为空则调用Consumer对象的accept()方法,不为空则不调用。

Optional.ofNullable(user).ifPresent(() -> {
    // 包装对象值为空执行的代码
    ……
});
get方法
public T get() {
    if (value == null) {
        throw new NoSuchElementException("No value present");
    }
    return value;
}

返回包装对象的值,若为空则抛出NoSuchElementException异常。

orElse方法
public T orElse(T other) {
    return value != null ? value : other;
}

若包装对象不是null则返回包装对象的实际值,若为null则返回指定值,即返回传入的参数。

Optional.ofNullable(user).orElse(new User("zhangsan", 20));
orElseGet方法
public T orElseGet(Supplier<? extends T> other) {
    return value != null ? value : other.get();
}

作用同orElse方法,只不过orElseGet的入参是Supplier对象,当Optional包装对象为null时返回指定值,返回值由Supplierget方法产生。

// name是一个String对象
Optional.ofNullable(name).orElseGet(() -> "zhangsan");
orElseThrow方法
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
    if (value != null) {
        return value;
    } else {
        throw exceptionSupplier.get();
    }
}

作用同orElseGet类似,当包装对象不为空时返回包装对象的值,为空时返回一个Throwable异常。

filter方法
public Optional<T> filter(Predicate<? super T> predicate) {
    Objects.requireNonNull(predicate);
    if (!isPresent())
        return this;
    else
        return predicate.test(value) ? this : empty();
}

过滤Optional的包装对象,当包装对象满足Predicate的条件时返回该Optional实例,否则返回包装对象为nullOptional实例。

// 过滤name为zhangsan的User,当不存在时打印提示信息
Optional.ofNullable(user)
    .filter(u -> u.getName.equals("zhangsan"))
    .ifPresent(() -> System.out.println("There is no zhangsan"));
map方法
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
    Objects.requireNonNull(mapper);
    if (!isPresent())
        return empty();
    else {
        return Optional.ofNullable(mapper.apply(value));
    }
}

map方法对包装对象进行函数运算(即调用Functionapply方法)后返回一个新的Optional实例对象,新Optional实例的包装对象可以时任意类型(不一定与原包装对象的类型保持一致)。

如果包装对象为null则依然返回一个包装对象为nullOptional实例,map可以无限级调用。

// 返回一个Optional<String>
Optional.of(user).map(u -> u.getName());
flatMap方法
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
    Objects.requireNonNull(mapper);
    if (!isPresent())
        return empty();
    else {
        return Objects.requireNonNull(mapper.apply(value));
    }
}

功能同map方法,二者的区别在mapperapply方法的返回值类型不同,map方法中的mapper返回值可以是任意类型,而flatMap中的mapper的返回值是Optional类型,map方法会自动封装返回一个Optional

// 返回一个Optional<String>,作用同map的例子
Optional.of(user).flatMap(u -> Optional.of(u.getName()));
解决NPE

在由Optional之前,我们会使用if…else…做防御性检查,以防止出现空指针异常。

public String getUserName(User user) {
  if(user == null) {
    return "unknown";
  }
  return user.getName();
}

有了Optional之后,我们可以这样做了……

public String getUserName(User user) {
  Optional<User> optional_user = Optional.ofNullable(user);
  if(!optional_user.isPresent()) {
    return "unknown";
  }
  return user.getName();
}

啊……这……

显然这两种方法并无本质区别,依然都有防御性检查,依然都不够简洁。只不过第二种方法利用isPresent方法替换了显示的null判断。

正确的姿势。

public String getUserName(User user) {
  // 善用map方法
  return Optional.ofNullable(user)
                 .map(u -> u.getName())
                 .orElse("unkonwn");
}

这个例子并不能完全展示Optional的实力,再看下面的示例。

/**
* 获取领导姓名,不使用Optional
* */
public String getBossName(User user) {
  if(user != null) {
    Company company = user.getCompany();
    if(company != null) {
      Boss boss = company.getBoss();
      if(boss != null) {
        return boss.getName();
      }
    }
  }
        
  return "unkonwn";
}

优雅的姿势。

/**
* 获取领导姓名,使用Optional
* */
public String getBossName(User user) {
  return Optional.ofNullable(user)
                 .map(u -> u.getCompany())
                 .map(c -> c.getBoss())
                 .map(b -> b.getName())
                 .orElse("unkonwn");
}

善用Optional会使我们的代码无比的优雅和简洁。

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

推荐阅读更多精彩内容