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》作者对于这个工具类的一些建议,可以说是一针见血,果然大神的眼光和经验是非常独到的。

相关文章
|
6天前
|
存储 监控 安全
JVM工作原理与实战(十六):运行时数据区-Java虚拟机栈
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了运行时数据区、Java虚拟机栈等内容。
11 0
|
7天前
|
Java
Java中的多线程编程:基础知识与实战技巧
【5月更文挑战第6天】多线程编程是Java中的一个重要特性,它允许我们在一个程序中同时执行多个任务。本文将介绍Java多线程的基础知识,包括线程的创建、启动、同步和通信,以及如何在Java中实现多线程编程。通过实例代码和解析,帮助读者深入理解Java多线程编程的概念和应用。
|
7天前
|
存储 算法 安全
Java工具类
Java工具类
29 5
Java工具类
|
11天前
|
存储 Java 数据格式
Java实战:轻松掌握文件重命名与路径提取技巧
Java实战:轻松掌握文件重命名与路径提取技巧
19 0
|
13天前
|
设计模式 算法 安全
Java多线程编程实战:从入门到精通
【4月更文挑战第30天】本文介绍了Java多线程编程的基础,包括线程概念、创建线程(继承`Thread`或实现`Runnable`)、线程生命周期。还讨论了线程同步与锁(同步代码块、`ReentrantLock`)、线程间通信(等待/通知、并发集合)以及实战技巧,如使用线程池、线程安全设计模式和避免死锁。性能优化方面,建议减少锁粒度和使用非阻塞算法。理解这些概念和技术对于编写高效、可靠的多线程程序至关重要。
|
14天前
|
XML Java 测试技术
Java异常处理神器:Guava Throwables类概念与实战
【4月更文挑战第29天】在Java开发中,异常处理是保证程序稳定性和可靠性的关键。Google的Guava库提供了一个强大的工具类Throwables,用于简化和增强异常处理。本篇博客将探讨Throwables类的核心功能及其在实战中的应用。
27 2
|
14天前
|
安全 Java 测试技术
利用Java反射机制提高Spring Boot的代码质量:概念与实战
【4月更文挑战第29天】Java反射机制提供了一种强大的方法来在运行时检查或修改类和对象的行为。在Spring Boot应用中,合理利用反射可以提高代码的灵活性和可维护性。本篇博客将探讨Java反射的核心概念,并展示如何通过反射提高Spring Boot项目的代码质量。
29 0
|
XML JSON JavaScript
干货:排名前 16 的 Java 工具类!
在Java中,工具类定义了一组公共方法,这篇文章将介绍Java中使用最频繁及最通用的Java工具类。以下工具类、方法按使用流行度排名,参考数据来源于Github上随机选取的5万个开源项目源码。
150 0
|
Java 数据安全/隐私保护 数据格式
干货:排名前16的Java工具类
image 在Java中,工具类定义了一组公共方法,这篇文章将介绍Java中使用最频繁及最通用的Java工具类。以下工具类、方法按使用流行度排名,参考数据来源于Github上随机选取的5万个开源项目源码。
4469 0
|
1天前
|
Java 调度
Java一分钟之线程池:ExecutorService与Future
【5月更文挑战第12天】Java并发编程中,`ExecutorService`和`Future`是关键组件,简化多线程并提供异步执行能力。`ExecutorService`是线程池接口,用于提交任务到线程池,如`ThreadPoolExecutor`和`ScheduledThreadPoolExecutor`。通过`submit()`提交任务并返回`Future`对象,可检查任务状态、获取结果或取消任务。注意处理`ExecutionException`和避免无限等待。实战示例展示了如何异步执行任务并获取结果。理解这些概念对提升并发性能至关重要。
16 5