建议收藏,Java Optional如何使用的最佳实践

真实业务场景

先来看这样一个业务中的场景:
在这里插入图片描述
这里取值可能为空,如果为空改成数字0L,如果交给你你怎么处理?
❌ 错误示范
你会不会把这个流拆开使用for循环,在用ifelse判断,如下:

if(item.getTaskRecordId != null) {
	// 处理逻辑
}

✅ 使用Optional轻松解决这种空值的问题

List<String> engineTaskIds = executeCaseRecords.stream()
        .filter(item -> !TaskStatus.isStatusFinished(item.getStatus()))
        .map(item -> SystemUtil.engineTaskId(Optional.ofNullable(item.getTaskRecordId()).orElse(0L),item.getCaseRecordId(), item.getId()))
        .collect(Collectors.toList());

Optional简介

Optional是Java8提供的为了解决null安全问题的一个API。善用Optional可以使我们代码中很多繁琐、丑陋的设计就可以变得十分优雅,比如上面的真实业务场景的案例。
在这里插入图片描述

方法列表

按照常用的顺序排列如下:

  • Optional.orElseGet(Supplier<? extends T> other) 如果存在则返回值,否则调用other并返回该调用的结果。
  • Optional.orElse(T other) 如果存在返回,不存在返回other
  • Optional.ofNullable(T value) value可以为空
  • Optional.empty() 创建一个空实例对象,不存在值。不存在值的意义不是里面存null,null也是值。
  • Optional.of(T value) 返回一个包含value的对象。value不能是null,否则会抛NPE异常
  • Optional.get() 返回非空值,如果value为空,抛出NoSuchElementException
  • Optional.isPresent() 值存在返回true,不存在返回false,存在null,也是false
  • Optional.ifPresent(Consumer<? super T> consumer) 如果存在值,则使用该值调用指定的消费者,否则什么都不做。
  • Optional.filter(Predicate<? super T> predicate) 如果存在一个值,并且该值与给定的Predicate匹配,则返回描述该值的Optional,否则返回空Optional。
  • Optional.map(Function<? super T, ? extends U> mapper) 如果存在值,则对其应用所提供的映射函数,如果结果非空,则返回描述结果的Optional。否则返回一个空的Optional。
  • Optional.flatMap(Function<? super T, Optional> mapper) 如果存在值,则对其应用所提供的与Optional相关的映射函数,返回该结果,否则返回空的Optional。这个方法类似于map(Function),但是所提供的映射器的结果已经是一个Optional,并且如果调用,flatMap不会用额外的Optional来包装它。
  • Optional.orElseThrow(Supplier<? extends X> exceptionSupplier) throws X 返回包含的值(如果存在),否则抛出由提供的提供程序创建的异常。

最常用到的方法是orElese()

演示示例

1. 创建Optional对象

方式一:创建个空对象

Optional<String> empty = Optional.empty();
System.out.println(empty.isPresent());  // false
System.out.println(empty.orElse("hello"));  // hello

方式二: 创建一个非空的 Optional 对象

Optional<String> opt = Optional.of("hello world");
System.out.println(opt); // 输出:Optional[hello world]

方式三:可以使用静态方法 ofNullable() 创建一个即可空又可非空的 Optional 对象

Optional<String> optOrNull = Optional.ofNullable(null);
System.out.println(optOrNull); // 输出:Optional.empty

2. 判断值是否存在

可以通过方法 isPresent() 判断一个 Optional 对象是否存在,如果存在,该方法返回 true,否则返回 false——取代了 obj != null 的判断。

Optional<String> opt = Optional.of("hello world");
System.out.println(opt.isPresent()); // 输出:true

Optional<String> optOrNull = Optional.ofNullable(null);
System.out.println(opt.isPresent()); // 输出:false

3. 非空表达式

Optional 类有一个非常现代化的方法——ifPresent(),允许我们使用函数式编程的方式执行一些代码,因此,我把它称为非空表达式。如果没有该方法的话,我们通常需要先通过 isPresent() 方法对 Optional 对象进行判空后再执行相应的代码:

Optional<String> optOrNull = Optional.ofNullable(null);
if (optOrNull.isPresent()) {
    System.out.println(optOrNull.get().length());
}

有了 ifPresent() 之后,情况就完全不同了,可以直接将 Lambda 表达式传递给该方法,代码更加简洁,更加直观。

Optional<String> opt = Optional.of("hello world");
opt.ifPresent(str -> System.out.println(str.length())); // 如果是null这里就不会执行了

4. 设置(获取)默认值

有时候,我们在创建(获取) Optional 对象的时候,需要一个默认值,orElse() 和 orElseGet() 方法就派上用场了。orElse() 方法用于返回包裹在 Optional 对象中的值,如果该值不为 null,则返回;否则返回默认值。该方法的参数类型和值得类型一致。

String name = Optional.ofNullable(null).orElse("hello world");
System.out.println(name); // 输出:hello world

orElseGet() 方法与 orElse() 方法类似,但参数类型不同。如果 Optional 对象中的值为 null,则执行参数中的函数。

String name = Optional.ofNullable(null).orElseGet(()->"hello world");
System.out.println(name); // 输出:hello world

从输出结果以及代码的形式上来看,这两个方法极其相似,但是需要注意一点的是orElese里的代码一定会执行但不一定返回,orEleseGet就不一定会执行,参考下面代码:

public static void main(String[] args) {
   String name = "hello";
   System.out.println("orElse");
   String name2 = Optional.ofNullable(name).orElse(getDefaultValue());

   System.out.println("orElseGet");
   String name3 = Optional.ofNullable(name).orElseGet(TaskCancelServiceImpl::getDefaultValue);

}

public static String getDefaultValue() {
  	System.out.println("getDefaultValue");
  	return "getDefaultValue";
}

输出内容:
orElse
getDefaultValue
orElseGet
可以看到orEleseGet部分内容没有执行。所以这种情况orElese方法性能更佳

5. 获取值

直观从语义上来看,get() 方法才是最正宗的获取 Optional 对象值的方法,但很遗憾,该方法是有缺陷的,因为假如 Optional 对象的值为 null,该方法会抛出 NoSuchElementException 异常。这完全与我们使用 Optional 类的初衷相悖,所以不建议使用get方法。

public class GetOptionalDemo {
    public static void main(String[] args) {
        String name = null;
        Optional<String> optOrNull = Optional.ofNullable(name);
        System.out.println(optOrNull.get());
    }
}

这段程序在运行时会抛出异常:

Exception in thread "main" java.util.NoSuchElementException: No value present
	at java.base/java.util.Optional.get(Optional.java:141)
	at com.cmower.dzone.optional.GetOptionalDemo.main(GetOptionalDemo.java:9)

尽管抛出的异常是 NoSuchElementException 而不是 NPE,但在我们看来,显然是在“五十步笑百步”。建议使用orElseGet() 方法获取 Optional 对象的值。

6. 过滤Filter使用

使用Optional可以用来检验参数的合法性

public void setName(String name) throws IllegalArgumentException {
    this.name = Optional.ofNullable(name)
                        .filter(User::isNameValid)
                        .orElseThrow(()->new IllegalArgumentException("Invalid username."));
}

7. 映射map的使用

先看下不使用map的代码:

public static String getChampionName(Competition comp) throws IllegalArgumentException {
    if (comp != null) {
        CompResult result = comp.getResult();
        if (result != null) {
            User champion = result.getChampion();
            if (champion != null) {
                return champion.getName();
            }
        }
    }
    throw new IllegalArgumentException("The value of param comp isn't available.");
}

使用map改造过后的代码是什么样的:

public static String getChampionName(Competition comp) throws IllegalArgumentException {
    return Optional.ofNullable(comp)
            .map(Competition::getResult)  // 相当于c -> c.getResult(),下同
            .map(CompResult::getChampion)
            .map(User::getName)
            .orElseThrow(()->new IllegalArgumentException("The value of param comp isn't available."));
}

参考对比会发现使用Optional可以优雅解决很多npe场景的一些问题。

总结

使用Optional,推荐使用方法:orElese,orEleseGet,ofNullable,filter和map。

  • 72
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java使用Kafka消费者的最佳实践可以通过以下步骤来完成: 1. 首先,确保你的Java项目中已经引入了Kafka的依赖。可以在pom.xml文件中添加以下依赖项: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-kafka</artifactId> </dependency> ``` 2. 创建一个Kafka消费者监听器类,并在类上添加`@Component`注解,以便让Spring容器管理它。在该类中使用`@KafkaListener`注解来指定要监听的Kafka主题和消费者组ID。例如,你可以使用以下代码示例: ```java @Component public class KafkaConsumerListener { @KafkaListener(topics = "your_topic_name", groupId = "your_consumer_group_id") public void consume(ConsumerRecord<?, ?> record) { Optional<?> value = Optional.ofNullable(record.value()); // 进行消息处理逻辑 System.out.println("print message: " + value); } } ``` 在这个示例中,`@KafkaListener`注解指定了要监听的Kafka主题名为"your_topic_name",消费者组ID为"your_consumer_group_id"。`consume`方法是用来处理接收到的消息的逻辑。 3. 配置Kafka相关的属性。在`application.properties`或`application.yml`文件中添加Kafka的配置项,例如: ```properties spring.kafka.bootstrap-servers=localhost:9092 ``` 这个配置项指定了Kafka的服务器地址和端口。 4. 最后,启动你的Java应用程序,它将自动创建一个Kafka消费者并开始监听指定的主题。当有新消息到达时,`consume`方法将被调用。 综上所述,这就是Java使用Kafka消费者的最佳实践。通过使用`@KafkaListener`注解来监听指定的主题,并在相应的方法中处理接收到的消息,你可以轻松地实现Kafka消费者的功能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [【项目实战】Java 开发 Kafka 消费者](https://blog.csdn.net/qq_37967783/article/details/131715447)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [Java实现Kafka生产者消费者代码实例](https://download.csdn.net/download/weixin_38681719/12725931)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值