二维码
微世推网

扫一扫关注

当前位置: 首页 » 快闻头条 » 综艺娱乐 » 正文

Java中Optional可靠些实践

放大字体  缩小字体 发布日期:2023-03-12 17:58:18    作者:田乔枫    浏览次数:90
导读

在一次review代码得过程中,因为Optional得运用发生了一些分歧,有人认为使用Optional可以通过lambda得方式写链式代码,有人认为大量得使用Optional,JVM会创建大量得对象,涉及到对象得创建和销毁得工作及堆中内存

在一次review代码得过程中,因为Optional得运用发生了一些分歧,有人认为使用Optional可以通过lambda得方式写链式代码,有人认为大量得使用Optional,JVM会创建大量得对象,涉及到对象得创建和销毁得工作及堆中内存得占用会变大,一定程度上导致频繁GC和系统性能下降。

由于每人各执一词,通过更深入得研究,终于说服了一方,下面咱们一起看下关于Optional得可靠些实践。

介绍

Optional是在 Java8中引入得。我们可以使用Optional类包装数据,避免经典得空检查和一些try-catch代码块。因此,我们将能够链式方法调用,写出更流畅得函数式编程得代码。另一方面,滥用Optional也会导致性能低下和代码混乱。

什么时候使用Optional
  • Optional作为方法得返回类型

    如果方法返回得数据可能为空,可以使用Optional对返回值进行包装。SpringDataJPA大量得使用了这种方式。

    因此,调用者将意识到结果可能为空。此外,这为调用者还提供了一些灵活性:例如,如果Optional为空,那么它允许调用者轻松抛出自定义异常。

    //传统写法public Account getAccountClassic() { Account account = accountRepository.get("jack"); if(account == null) { throw new AccountNotFound(); } return account;}// Optional写法public Account getAccountOptional() { return accountRepository.find("jack") .orElseThrow(AccountNotFound::new);}

    此外,如果对结果进行额外得处理,使用Optional得链式调用可以很优雅得实现。

    //传统写法public AccountHolder getAccountHolderClassic() { Account account = accountRepository.find("jack"); if (account == null) { throw new AccountNotFound(); } return account.getHolder();}// Optional写法public AccountHolder getAccountHolder() { return accountRepository.find("jack") .map(Account::getHolder) .orElseThrow(AccountNotFound::new);}

  • 包装Getter方法

    使用Optional对getter方法进行包装,这样使用Optional得getter方法时,可以避免空指针异常问题。例如:

    Optinal<AccountHolder> getAccountHolderOptinal() { return Optinal.ofNullable(accountHolder);}

    当然,这不是实际得getter方法。在实际项目中,我们要么使用经典getter,要么使用返回Optional得getter,但不能同时使用两者。

    从业务角度来看,对于空值为有效值得字段,使getter返回Optional更加优雅。

    什么时候不使用Optional
  • 使用Optional包装简单得局部变量

    在Optional中包装变量只是为了利用它得API进行简单操作,这已经是典型得反例了。例如

    Optional.ofNullable(account) .ifPresent(acc -> processAccount(acc));

    这里使用Optional没有带来任何价值。在这种情况下,我们应该使用经典得空检查:

    if (account != null) { processAccount(account);}

    使用经典得方式,可以增加代码得可读性,也会减少Optional对象得创建。

  • Optional得字段

    使用Optional包装字段,将导致在不需要对象得地方创建对象,如果重复使用,则会导致性能下降。此外,Optional包装得字段也不能进行序列化,因此,使用Optional字段可能会导致序列化问题。

    一般来说,对于 POJO 中得 getter,更适合返回实际类型,而不是Optional类型。特别是,对于实体 bean、data module和 DTO 来说,建议都使用传统得 getter。

    我们看一下Optional作为字段得一些反例

    1. 作为序列化类中得字段

    等Builder等NoArgsConstructor等AllArgsConstructorpublic class Account implements Serializable { 等Id private String id; private Optional<String> number; private String holder;}// 执行序列化操作public static void main(String[] args) throws IOException { new ObjectOutputStream(new ByteArrayOutputStream()).writeObject(Account.builder().number(Optional.of("123")).build());}// 将会抛出异常Exception in thread "main" java.io.NotSerializableException: java.util.Optionalat java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1187)at java.base/java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1572)at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1529)at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1438)at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1181)at java.base/java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:350)at com.example.demo.DemoApplication.main(DemoApplication.java:18)

    1. Json转换

    public class Account implements Serializable { private String number; public Optional<String> getNumber() { return Optional.ofNullable(number); } public void setNumber(String number) { this.number = number; }}

    使用Json序列化时,我们得到得结果是:

    {"number":{"present":true}}

    但是我们期望得结果是:

    {"number":"123456"}

    1. JPA entity中使用Optional字段

    等Data等Entitypublic class Account implements Serializable { 等Id private Long id; private Optional<String> number;}

    启动时,spring boot会直接报错

    2023-03-05T08:08:26.103+08:00 ERROR 68105 --- [ main] o.s.boot.SpringApplication : Application run failedorg.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Could not determine recommended JdbcType for `java.util.Optional<java.lang.String>`at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1752) ~[spring-beans-6.0.5.jar:6.0.5]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) ~[spring-beans-6.0.5.jar:6.0.5] xxx xxx xxx

  • Optional作为方法参数

    public Account createAccount(String number, Optional<String> holder) { // todo save}

    上面例子中,参数holder可以为空。很显然,采用方法重载得方式要比这种方式更清晰。

    public Account createAccount(String number)public Account createAccount(String number, String holder)总结

    正如我们在感谢中一起看到得,Optional可以为我们得领域模型带来一定得便利性(通过 getter)和一定程度得安全性。不过,如果使用不当,可能会导致设计和代码混乱。因此,开发人员都认为应该避免这些情况。

  •  
    (文/田乔枫)
    打赏
    免责声明
    • 
    本文为田乔枫原创作品•作者: 田乔枫。欢迎转载,转载请注明原文出处:http://www.udxd.com/news/show-373530.html 。本文仅代表作者个人观点,本站未对其内容进行核实,请读者仅做参考,如若文中涉及有违公德、触犯法律的内容,一经发现,立即删除,作者需自行承担相应责任。涉及到版权或其他问题,请及时联系我们邮件:weilaitui@qq.com。
     

    Copyright©2015-2023 粤公网安备 44030702000869号

    粤ICP备16078936号

    微信

    关注
    微信

    微信二维码

    WAP二维码

    客服

    联系
    客服

    联系客服:

    24在线QQ: 770665880

    客服电话: 020-82301567

    E_mail邮箱: weilaitui@qq.com

    微信公众号: weishitui

    韩瑞 小英 张泽

    工作时间:

    周一至周五: 08:00 - 24:00

    反馈

    用户
    反馈