JAVA8实战 - Optional工具类(下)

简介: JAVA8实战 - Optional工具类(下)

Optional的使用场景



从个人角度来说其实使用不是很方便,下面说下Optional的工具使用场景:


1. 封装可能为Null的值


还是和所说的Map类似,当我们使用下面的操作获取值的时候,就很容易引发null:


User user = (User)map.get("user");
复制代码


于是,Optional的用处就派上了:


Optional<Object> value = Optional.ofNullable(map.get("user"));
复制代码


2. 异常和optional的对比


通常情况下我们会使用捕获异常的方式进行异常的处理,下面是一个常见的字符串转Int的方法,一般情况下我们都会用try/catch防止空指针或者转化异常,除非我们可以保证数据的准确性:


String str = "s";
try{
    Integer.parseInt(str);
}catch(Exception e){
    // ....
}
复制代码


使用Optional之后可以进行如下的改造,我们将得到一个安全的Optional进行操作,而不是一个可能存在隐式的NullPointException问题代码:


public static Optional<Integer> str2Int(String str) {
    try {
        return Optional.of(Integer.parseInt(str));
    } catch (NumberFormatException e) {
        return Optional.empty();
    }
}
复制代码


3. 建议将Optional封装到一个工具类当中:


比如封装成下面这种的简单方法:


private static Optional<Integer> str2Int(String str) {
    try {
        return Optional.of(Integer.parseInt(str));
    } catch (NumberFormatException e) {
        return Optional.empty();
    }
}
复制代码


4. 实战:读取Properties值


这里直接把书里的案例拿来用了,这个方法主要作用是读取一个配置文件的int值,当读取不到内容的时候,自动给默认值0。


private static Optional<Integer> str2Int(String str) {
    try {
        return Optional.of(Integer.parseInt(str));
    } catch (NumberFormatException e) {
        return Optional.empty();
    }
}
public static int read(Properties properties, String name) {
    return Optional.ofNullable(properties.getProperty(name))
        .flatMap(OptionalTest::str2Int)
        .filter(i -> i > 0)
        .orElse(0);
}
复制代码


5. 其他测试:


下面是个人学习的时候一些简单案例尝试:


/**
     * 实际使用场景
     * 1. 我们要将一个对象的名称全部统一为大写,防止空指针. 但是实际使用来看还是遇到了不少的问题
     */
    @Test
    public void actualUse() {
        User user = new User();
        // java.lang.NullPointerException 如果编程习惯不好,这种工具类其实并不能解决问题
//        Optional.ofNullable(user).ifPresent(u->{
//            String toLowerCase = u.getName().toLowerCase();
//            u.setName(toLowerCase);
//        });
        // 正确的使用方式应该是如下的形式:
        // 下面的语句放开注释运行打印结果为: SSS
//        user.setName("sss");
        // 如果为null会抛出 java.lang.RuntimeException
//        String s = Optional.ofNullable(user).map(User::getName).map(String::toUpperCase).orElseThrow(RuntimeException::new);
//        System.out.println(s);
        // 我们也可以用另一种方式
        String s2 = Optional.ofNullable(user).map(User::getName).map(String::toUpperCase).orElse("默认值");
        System.out.println(s2);
        // 我们要对一个前端传入的值进行split或者substring的时候
        // 案例数据除开分隔符有差异之外无任何差异
        String tags1 = "标签1,标签2,标签3,标签4";
        String tags2 = "标签1,标签2,标签3,标签4";
        String[] strings1 = Optional.of(tags1).map(tg -> tags1.split(",")).get();
        System.out.println(strings1[0]);
        String[] strings2 = Optional.of(tags2).map(tg -> tags2.split(",")).get();
        String[] strings3 = Optional.ofNullable(tags2).map(StringUtils::isNotBlank).map(tg -> tags2.split(",")).orElse(new String[]{"ss"});
        System.out.println(strings2[0]);
        System.out.println(Arrays.toString(strings3));
        // 运行结果,如果此时有值,基本无问题
        // 标签1
        // 标签1,标签2,标签3,标签4
        // 如果上面的案例当中,传入的为null会如何?
        // 所以我们需要修改上面的格式,确保不管tag的值是否存在,都可以只关心我们具体需要操作的数据
        // 如果为null,则没有任何结果处理。我们可以使用map进行各种操作
//        String tags3 = "null,222";
        String tags3 = "a,b|C|d";
        Optional.ofNullable(tags3).map(tg -> tags3.split(",")).map(tg -> {
            for (int i = 0; i < tg.length; i++) {
                tg[i] = tg[i].toUpperCase();
            }
            return tg;
        }).ifPresent(item -> {
            for (String s : item) {
                System.out.println(s);
            }
        });
        // JSON解析的数据失败或者传入的格式不对导致的NULL
        System.out.print("\nJSON解析的数据失败或者传入的格式不对导致的NULL");
        Object parse = JSON.parseObject("{name:\"13\"}", User.class);
        System.out.println(parse);
        Optional.ofNullable("{age:\"1\"}")
                .map(obj -> JSON.parseObject(obj, User.class))
                .ifPresent(System.out::println);
    }/*运行结果
    默认值
    标签1
    标签1,标签2,标签3,标签4
    A
    B|C|D
    JSON解析的数据失败或者传入的格式不对导致的NULLcom.xxx.interview.jdk8.OptionalTest$User@548b7f67
    com.xxx.interview.jdk8.OptionalTest$User@1810399e
    */
复制代码


《Effective java》第55条建议


感兴趣的可以直接跳转下面这些链接(在线阅读网站貌似点不进去,所以只有在线PDF网址了...)

itmyhome.com

以防万一这里再补一个百度链接(如果公众号无法点击,请阅读原文获取)

链接:pan.baidu.com/s/1kQ8EHTn7…提取码:vkv3

既然提到了Optional的用法,这里也一并谈谈关于《Effective java》是如何看待这一个工具类的,这一条的标题是:谨慎返回Optional

从个人的角度来看,Optional的本质作用是:提供了结果的可扩展性以及提供给调用方更多的可操作性,比如调用方可以使用此来按照以前的判断null方式处理if(obj.isPresent()),或者使用新式的Lambda操作,比如像下面这样,如果存在值则进行打印的操作,否则什么事情都不会发生:


Optional.ofNullable("{age:\"1\"}")
                .map(obj -> JSON.parseObject(obj, User.class))
                .ifPresent(System.out::println);
复制代码


还有一点需要注意的是get()这个方法,如果不能确保值确实存在,建议谨慎或者避开这个方法,因为一旦为null此方法会抛出一个空指针异常。

另外,在之前讲述的方法:orElseThrow这个方法传入的是一个异常工厂而不是真正的异常。

后面主要提到的是一些Java9的操作,由于本文只涉及Java8的版本,所以更高版本的内容可以从《Effective Java》这本书里面看到。

下面是作者关于optional的一些个人建议以及编程禁忌:


几点警告


1. 永远不要使用Optional返回Null


首先,该书作者也是提到了Optional在日常的编码工作当中如何使用它来规避一些可能存在的null对象操作,同时提出一个重要的禁忌:永远不要用Optional的返回值返回null,比如我们将上面练习当中的代码改为下面的方式:


private static Optional<Integer> str2Int(String str) {
    try {
        return Optional.of(Integer.parseInt(str));
    } catch (NumberFormatException e) {
        // 千万不要这么做
        return null;
    }
}
复制代码


2. 不要使用包装基本类型的Optional


设计Optional的设计师在考虑的时候,为基础类型也设置了专属的Optional类,然而作者认为这三个类的设计很垃圾,并且建议永远不要返回基本包装类型,这里验证了一下,发现少了确实少了不少方法,比如:ofNullable这个方法,这会直接导致你在编写代码的时候增加不必要的判断,并且无法互相兼容,我想这也是作者不推荐的原因之一吧。


  • OptionalDouble
  • OptionalInt
  • OptionalLong


3. 不要把optional作为映射或者键值


这也很好理解,比如像下面这样:


Map<Optional<String>, Optional<Object>> map = // .....
复制代码


这并不会让你少些代码,反而会增加代码的理解难度和程序的复杂度,所以不建议把Optional用作任何的键值对或者集合的元素当中。


什么时候应该使用?


一句话:**如果无法返回结果并且返回结果的客户端必须处理的时候,就应该声明Optional\<T\>。**
复制代码


哪些情况不适用?


其实上面的警告也提到了一部分内容:

  1. 非常注重性能的场合:因为Optional的封装以及类似“流”的操作需要额外的内存开销,所以不适合一些十分注重性能的情况。
  2. 需要使用键值对或者集合元素的场合:原因在上文说了,这里不再赘述。
  3. 包装基本类型的Optional:设计缺陷,对比源代码就知道了。


总结:


总之Optional这个工具还是具备一定的实用价值的,这里非常喜欢《Effective Java》作者对于这个工具类的一些建议,可以说是一针见血,果然大神的眼光和经验是非常独到的。

相关文章
|
2月前
|
存储 Java 开发者
Java Map实战:用HashMap和TreeMap轻松解决复杂数据结构问题!
【10月更文挑战第17天】本文深入探讨了Java中HashMap和TreeMap两种Map类型的特性和应用场景。HashMap基于哈希表实现,支持高效的数据操作且允许键值为null;TreeMap基于红黑树实现,支持自然排序或自定义排序,确保元素有序。文章通过具体示例展示了两者的实战应用,帮助开发者根据实际需求选择合适的数据结构,提高开发效率。
63 2
|
9天前
|
安全 Java 开发者
Java 多线程并发控制:深入理解与实战应用
《Java多线程并发控制:深入理解与实战应用》一书详细解析了Java多线程编程的核心概念、并发控制技术及其实战技巧,适合Java开发者深入学习和实践参考。
|
2月前
|
存储 消息中间件 安全
JUC组件实战:实现RRPC(Java与硬件通过MQTT的同步通信)
【10月更文挑战第9天】本文介绍了如何利用JUC组件实现Java服务与硬件通过MQTT的同步通信(RRPC)。通过模拟MQTT通信流程,使用`LinkedBlockingQueue`作为消息队列,详细讲解了消息发送、接收及响应的同步处理机制,包括任务超时处理和内存泄漏的预防措施。文中还提供了具体的类设计和方法实现,帮助理解同步通信的内部工作原理。
JUC组件实战:实现RRPC(Java与硬件通过MQTT的同步通信)
|
2月前
|
开发框架 Java 程序员
揭开Java反射的神秘面纱:从原理到实战应用!
本文介绍了Java反射的基本概念、原理及应用场景。反射允许程序在运行时动态获取类的信息并操作其属性和方法,广泛应用于开发框架、动态代理和自定义注解等领域。通过反射,可以实现更灵活的代码设计,但也需注意其性能开销。
47 1
|
2月前
|
算法 搜索推荐 Java
java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
这篇文章介绍了如何使用Java后端技术,结合Graphics2D和Echarts等工具,生成包含个性化信息和图表的海报,并提供了详细的代码实现和GitHub项目链接。
111 0
java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
|
2月前
|
Java
Java 些许公共工具类
Java 些许公共工具类
16 1
|
3月前
|
Java 数据中心 微服务
Java高级知识:线程池隔离与信号量隔离的实战应用
在Java并发编程中,线程池隔离与信号量隔离是两种常用的资源隔离技术,它们在提高系统稳定性、防止系统过载方面发挥着重要作用。
53 0
|
3月前
|
消息中间件 缓存 Java
RocketMQ的JAVA落地实战
RocketMQ作为一款高性能、高可靠、高实时、分布式特点的消息中间件,其核心作用主要体现在异步处理、削峰填谷以及系统解耦三个方面。
178 0
|
机器人 Java
深度解析 Java 的 Optional 类(下)
深度解析 Java 的 Optional 类(下)
116 0
深度解析 Java 的 Optional 类(下)
|
Java
深度解析 Java 的 Optional 类(上)
深度解析 Java 的 Optional 类(上)
136 0
深度解析 Java 的 Optional 类(上)