Rumah Java javaTutorial 使用 Java8 Optional 的正确姿势

使用 Java8 Optional 的正确姿势

Jan 18, 2017 pm 03:24 PM

Java程序设计语言

java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由Sun Microsystems公司于1995年5月推出的Java程序设计语言和Java平台(即JavaEE(j2ee), JavaME(j2me), JavaSE(j2se))的总称。


我们知道 Java 8 增加了一些很有用的 API, 其中一个就是 Optional. 如果对它不稍假探索, 只是轻描淡写的认为它可以优雅的解决 NullPointException 的问题, 于是代码就开始这么写了

Optional<User> user = ……
if (user.isPresent()) {
return user.getOrders();
} else {
return Collections.emptyList();
}
Salin selepas log masuk

那么不得不说我们的思维仍然是在原地踏步, 只是本能的认为它不过是 User 实例的包装, 这与我们之前写成

User user = …..
if (user != null) {
return user.getOrders();
} else {
return Collections.emptyList();
}
Salin selepas log masuk

实质上是没有任何分别. 这就是我们将要讲到的使用好 Java 8 Optional 类型的正确姿势.

在里约奥运之时, 新闻一再提起五星红旗有问题, 可是我怎么看都看不出来有什么问题, 后来才道是小星星膜拜中央的姿势不对. 因此我们千万也别对自己习以为常的事情觉得理所当然, 丝毫不会觉得有何不妥, 换句话说也就是当我们切换到 Java 8 的 Optional 时, 不能继承性的对待过往 null 时的那种思维, 应该掌握好新的, 正确的使用 Java 8 Optional 的正确姿势.

直白的讲, 当我们还在以如下几种方式使用 Optional 时, 就得开始检视自己了

调用 isPresent() 方法时

调用 get() 方法时

Optional 类型作为类/实例属性时

Optional 类型作为方法参数时

isPresent() 与 obj != null 无任何分别, 我们的生活依然在步步惊心. 而没有 isPresent() 作铺垫的 get() 调用在 IntelliJ IDEA 中会收到告警

Reports calls to java.util.Optional.get() without first checking with a isPresent() 
call if a value is available. If the Optional does not contain a value, get() will throw an exception. 
(调用 Optional.get() 前不事先用 isPresent() 检查值是否可用. 假如 Optional 不包含一个值, get() 将会抛出一个异常)
Salin selepas log masuk

把 Optional 类型用作属性或是方法参数在 IntelliJ IDEA 中更是强力不推荐的

Reports any uses of java.util.Optional<T>, java.util.OptionalDouble, java.util.OptionalInt, 
java.util.OptionalLong or com.google.common.base.Optional as the type for a field or a parameter. Optional was designed
 to provide a limited mechanism for library method return types where there needed to be a clear way to represent 
 “no result”. Using a field with type java.util.Optional is also problematic if the class needs to be Serializable, 
 which java.util.Optional is not. (使用任何像 Optional 的类型作为字段或方法参数都是不可取的. Optional 只设计为类库方法的,
  可明确表示可能无值情况下的返回类型. Optional 类型不可被序列化, 用作字段类型会出问题的)
Salin selepas log masuk

所以 Optional 中我们真正可依赖的应该是除了 isPresent() 和 get() 的其他方法:

public Optional map(Function mapper)

public T orElse(T other)

public T orElseGet(Supplier other)

public void ifPresent(Consumer consumer)

public Optional filter(Predicate predicate)

public Optional flatMap(Function> mapper)

public T orElseThrow(Supplier exceptionSupplier) throws X

我略有自信的按照它们大概使用频度对上面的方法排了一下序.

先又不得不提一下 Optional 的三种构造方式: Optional.of(obj), Optional.ofNullable(obj) 和明确的 Optional.empty()

Optional.of(obj): 它要求传入的 obj 不能是 null 值的, 否则还没开始进入角色就倒在了 NullPointerException 异常上了.

Optional.ofNullable(obj): 它以一种智能的, 宽容的方式来构造一个 Optional 实例. 来者不拒, 传 null 进到就得到 Optional.empty(), 非 null 就调用 Optional.of(obj).

那是不是我们只要用 Optional.ofNullable(obj) 一劳永逸, 以不变应二变的方式来构造 Optional 实例就行了呢? 那也未必, 否则 Optional.of(obj) 何必如此暴露呢, 私有则可?

我本人的观点是: 1. 当我们非常非常的明确将要传给 Optional.of(obj) 的 obj 参数不可能为 null 时, 比如它是一个刚 new 出来的对象(Optional.of(new User(...))), 或者是一个非 null 常量时; 2. 当想为 obj 断言不为 null 时, 即我们想在万一 obj 为 null 立即报告 NullPointException 异常, 立即修改, 而不是隐藏空指针异常时, 我们就应该果断的用 Optional.of(obj) 来构造 Optional 实例, 而不让任何不可预计的 null 值有可乘之机隐身于 Optional 中.

现在才开始怎么去使用一个已有的 Optional 实例, 假定我们有一个实例 Optional user, 下面是几个普遍的, 应避免 if(user.isPresent()) { ... } else { ... } 几中应用方式.

存在即返回, 无则提供默认值

return user.orElse(null);  //而不是 return user.isPresent() ? user.get() : null;
return user.orElse(UNKNOWN_USER);
Salin selepas log masuk

存在即返回, 无则由函数来产生

return user.orElseGet(() -> fetchAUserFromDatabase()); //而不要 return user.isPresent() ? user: fetchAUserFromDatabase();
Salin selepas log masuk

存在才对它做点什么

user.ifPresent(System.out::println);

//而不要下边那样
if (user.isPresent()) {
  System.out.println(user.get());
}
Salin selepas log masuk

map 函数隆重登场

当 user.isPresent() 为真, 获得它关联的 orders, 为假则返回一个空集合时, 我们用上面的 orElse, orElseGet 方法都乏力时, 那原本就是 map 函数的责任, 我们可以这样一行

return user.map(u -> u.getOrders()).orElse(Collections.emptyList())

//上面避免了我们类似 Java 8 之前的做法
if(user.isPresent()) {
  return user.get().getOrders();
} else {
  return Collections.emptyList();
}
Salin selepas log masuk

map 是可能无限级联的, 比如再深一层, 获得用户名的大写形式

return user.map(u -> u.getUsername())
           .map(name -> name.toUpperCase())
           .orElse(null);
Salin selepas log masuk

这要搁在以前, 每一级调用的展开都需要放一个 null 值的判断

User user = .....
if(user != null) {
  String name = user.getUsername();
  if(name != null) {
    return name.toUpperCase();
  } else {
    return null;
  }
} else {
  return null;
}
Salin selepas log masuk

针对这方面 Groovy 提供了一种安全的属性/方法访问操作符 ?.

user?.getUsername()?.toUpperCase();
Salin selepas log masuk

Swift 也有类似的语法, 只作用在  Optional 的类型上.

用了 isPresent() 处理 NullPointerException 不叫优雅, 有了  orElse, orElseGet 等, 特别是 map 方法才叫优雅.

其他几个, filter() 把不符合条件的值变为 empty(),  flatMap() 总是与 map() 方法成对的,  orElseThrow() 在有值时直接返回, 无值时抛出想要的异常.

一句话小结: 使用 Optional 时尽量不直接调用 Optional.get() 方法, Optional.isPresent() 更应该被视为一个私有方法, 应依赖于其他像 Optional.orElse(), Optional.orElseGet(), Optional.map() 等这样的方法.

最后, 最好的理解 Java 8 Optional 的方法莫过于看它的源代码 java.util.Optional, 阅读了源代码才能真真正正的让你解释起来最有底气, Optional 的方法中基本都是内部调用  isPresent() 判断, 真时处理值, 假时什么也不做.

以上就是使用 Java8 Optional 的正确姿势的内容,更多相关内容请关注PHP中文网(www.php.cn)!


Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

Video Face Swap

Video Face Swap

Tukar muka dalam mana-mana video dengan mudah menggunakan alat tukar muka AI percuma kami!

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Bagaimana untuk mengira tarikh setahun yang lalu atau setahun kemudian di Java 8? Bagaimana untuk mengira tarikh setahun yang lalu atau setahun kemudian di Java 8? Apr 26, 2023 am 09:22 AM

Java8 mengira tarikh setahun yang lalu atau setahun kemudian menggunakan kaedah tolak() untuk mengira tarikh setahun yang lalu packagecom.shxt.demo02;importjava.time.LocalDate;importjava.time.temporal.ChronoUnit;publicclassDemo09{publicstaticvoidmain(String[ ]args ){LocalDatetoday=LocalDate.now();LocalDatepreviousYear=today.minus(1,ChronoUni

Apakah kaedah Pilihan yang biasa digunakan di Jawa? Apakah kaedah Pilihan yang biasa digunakan di Jawa? May 09, 2023 am 11:34 AM

Prakata Pilihan dalam Java ialah objek bekas, yang boleh mengandungi nilai bukan nol atau kosong. Tujuan utamanya adalah untuk mengelakkan pengecualian penuding nol semasa menulis kod. Penggunaan lengkap Optional dalam java8 adalah seperti berikut: 1. Cipta objek Pilihan Anda boleh mencipta objek Pilihan yang mengandungi nilai bukan nol melalui kaedah of(), contohnya: Optionaloptional=Optional.of("value"). ; Ia juga boleh dibuat melalui kaedah ofNullable() Objek Pilihan yang mengandungi nilai mungkin nol, contohnya: Optionaloptional=Optiona

Bagaimana untuk mengira tarikh seminggu kemudian menggunakan Java 8? Bagaimana untuk mengira tarikh seminggu kemudian menggunakan Java 8? Apr 21, 2023 pm 11:01 PM

Cara mengira tarikh seminggu kemudian dalam Java8 Contoh ini akan mengira tarikh seminggu kemudian. Tarikh LocalDate tidak mengandungi maklumat masa kaedah tambah() digunakan untuk menambah hari, minggu dan bulan Kelas ChronoUnit mengisytiharkan unit masa ini. Memandangkan LocalDate juga merupakan jenis yang tidak boleh diubah, anda mesti menggunakan pembolehubah untuk menetapkan nilai selepas kembali. packagecom.shxt.demo02;importjava.time.LocalDate;importjava.time.temporal.ChronoUnit;publicclassDemo08{publicstaticvoidmain(String[

Bagaimana untuk menggunakan kelas Jam dalam Java8 Bagaimana untuk menggunakan kelas Jam dalam Java8 Apr 25, 2023 pm 03:37 PM

Kelas Jam Java8 Java8 menambah kelas Jam untuk mendapatkan cap waktu semasa, atau maklumat tarikh dan masa dalam zon waktu semasa. Apabila System.currentTimeInMillis() dan TimeZone.getDefault() digunakan sebelum ini, ia boleh digantikan dengan Jam. packagecom.shxt.demo02;importjava.time.Clock;publicclassDemo10{publicstaticvoidmain(String[]args){//Returnsthecurrenttimebase

Bagaimana untuk mengendalikan kes nilai nol menggunakan fungsi Pilihan dalam Java Bagaimana untuk mengendalikan kes nilai nol menggunakan fungsi Pilihan dalam Java Oct 20, 2023 am 10:06 AM

Cara menggunakan fungsi Pilihan untuk mengendalikan nilai nol dalam Java Dalam pengaturcaraan Java, kita sering menghadapi situasi di mana nilai nol dikendalikan. Pengecualian penunjuk nol ialah ralat biasa Untuk mengelakkan situasi ini, Java8 memperkenalkan kelas Pilihan untuk mengendalikan situasi nilai nol. Kelas Pilihan ialah kelas kontena yang boleh mengandungi nilai bukan kosong atau tiada nilai. Menggunakan kelas Pilihan, kita boleh mengendalikan situasi nilai nol dengan lebih anggun dan mengelakkan pengecualian penuding nol. bawah

Bagaimana untuk menangani zon waktu di Java8 Bagaimana untuk menangani zon waktu di Java8 Apr 27, 2023 pm 09:22 PM

Mengendalikan zon waktu di Java 8 Java 8 bukan sahaja memisahkan tarikh dan masa, tetapi juga memisahkan zon waktu. Kini terdapat satu siri kelas berasingan seperti ZoneId untuk mengendalikan zon waktu tertentu dan ZoneDateTime untuk mewakili masa dalam zon waktu tertentu. Ini dilakukan oleh kelas GregorianCalendar sebelum Java8. Contoh berikut menunjukkan cara untuk menukar masa dalam zon waktu ini kepada masa dalam zon waktu lain. packagecom.shxt.demo02;importjava.time.LocalDateTime;importjava.time.ZoneId;importjava.time.ZonedDateT

Bagaimana untuk mendapatkan cap masa semasa dalam Java8 Bagaimana untuk mendapatkan cap masa semasa dalam Java8 May 01, 2023 am 11:46 AM

Dapatkan cap masa semasa dalam Java8. Kelas Instant mempunyai kaedah kilang statik now() yang akan mengembalikan cap masa semasa, seperti yang ditunjukkan di bawah: packagecom.shxt.demo02;importjava.time.Instant;publicclassDemo16{publicstaticvoidmain(String[]args) {Instanttimestamp=Instant.now();System.out.println("Whatisvalueofthisinstant"+timestamp.t

Cara menggunakan alat pemformatan yang telah ditetapkan untuk menghuraikan atau memformat tarikh dalam Java8 Cara menggunakan alat pemformatan yang telah ditetapkan untuk menghuraikan atau memformat tarikh dalam Java8 Apr 28, 2023 pm 07:40 PM

Cara menggunakan alat pemformatan yang dipratentukan untuk menghuraikan atau memformat tarikh dalam Java 8 packagecom.shxt.demo02;importjava.time.LocalDate;importjava.time.format.DateTimeFormatter;publicclassDemo17{publicstaticvoidmain(String[]args){StringdayAfterTommorrow="201 ";80200 LocalDateformatted=LocalDate.parse

See all articles