• 欢迎光临~

让 Java 8 改进我们的程序

开发技术 开发技术 2022-07-21 次浏览

这些文字写的很迟,因为 Java 8 已经发布六年有余。

本文不是 Java 语法或 Java 8 新特性教程,故不涉及系统、细致的用法介绍而是只包含 Java 8 对旧程序的改进措施。

摘要

本文叙述了 Java 8 提供的新特性对现有代码的改进方式以及带来的好处。主要涉及比较重要的新特性:Optional、接口的默认方法、函数式、stream、CompletableFuture。

改进内容

Optional,更好的判别 null 的方式

Optional.ofNullable 可以用来构造一个可能为 null 的 Optional 对象

public void myFunc(Tea tea) {
    Optional.ofNullable(tea).map(Tea::getName).orElse("锡兰红茶");
    // ......
}

值得注意的是,一般情况下我们不应该使用它的 isPresent 或 isEmpty 方法,不然就和以前的 if (a == null) 方式一样了。

真正应该被关注的方法有:

  • orElse 或 orElseGet
  • filter
  • map
  • flatMap

这些方法灵活配合,可以实现嵌套判空、链式处理,又省事又不容易出错。

给接口添加默认方法(default method)简化代码

在 Java 8 中,我们可以给结构添加默认的方法,如此一来,就不必让每个实现类都重写(override)一下这个方法。

最简单的例子就是 JDK 中的 Iterable,它提供默认方法 forEach,顾名思义,对容器中每个元素进行某种动作。这样的方法显然没必要让每种容器(如 List,Queue)都重写。

另一个例子是数据库连接池 Druid,它在 1.2 版本开始用默认方法简化了很多代码。

值得注意的问题是接口的多继承(多实现)问题,本来,Java 不允许类多继承,避免了 C++ 中菱形继承的问题,默认方法的特性又把这个问题带回了 Java。解决办法是显示重写方法。

interface A {
    default void foo() {
        System.out.println("A::foo");
    }
}

interface B {
    default void foo() {
        System.out.println("B::foo");
    }
}

class C implements A, B {

    // C 必须重写 foo
    // 不然 Java 编译器不知道咋弄

    @Override
    public void foo() {
        System.out.println("C::foo");
    }
}

public class App {
    public static void main(String[] args) {
        A a = new C();
        a.foo(); // C::foo
    }
}

在恰当的时候使用 lambda 表达式与流式操作

一些临时函数应该用 lambda 表达式来代替。与旧式写法相比,少了污染视线的工具类(碍事)。

在“对一组对象进行一系列工序”的场景下,相比于旧式写法,用流式操作可以使程序简洁、更可读、更不易出错:

例 1:把用逗号分隔的多个职位(positions)中的每个职位转换到数字,然后取最大值。

final String[] ps = positions.split(",");
Arrays.stream(ps)
    .map(PositionToNumber::calc) // calc 是一个把 String 转换成 long 的函数
    .max(Long::compareTo)
    .orElse(Long.MIN_VALUE);

例 2:把所有雇员按照职位级别排倒序。

employeeList
    .stream()
    .sorted(Comparator.comparing(Employee::getCachePositionLevel).reversed())
    .collect(Collectors.toList());

永远使用新的日期时间 API

java.time.* 中的类(LocalDate,LocalTime,LocalDateTime 等)都是不可变的,所以完全不用担心并发安全问题。时间之间的加减都会返回新对象而非修改已有对象。格式也统一为 ISO-8601,不再有乱七八糟的各种格式。

of、with 系列的方法直观好用。星期几、月份也改成了枚举。一系列细节上的优点令人舒坦。

JDBC 也规定了数据库中日期时间相关类型也 Java 中日期时间类型的对应关系:

SQL Java
DATE LocalDate
TIME LocalTime
DATETIME LocalDateTime
TIMESTAMP LocalDateTime

并发工具类 StampedLock 的合理运用

在读多写少的时候,StampedLock 比 ReadWriteLock 性能更好,但它不支持重入,不支持条件变量。

另外,在 StampedLock 在上锁后切勿调用线程的 interrupt,不然 CPU 使用率会立刻上去。

灵活选用 CompletableFuture 和 RxJava 实现异步

在 Java 中表示异步操作的传统方式和大多数编程语言一样:回调函数(方法)。但一旦这么做,代码会变得和乱柴火垛似的,维护代价极大,还容易藏着一些隐晦的 BUG。

Java 8 为我们提供了新方法。

在异步场景不过于复杂时,CompletableFuture 是极好的异步操作工具。

CompletableFuture 的静态方法 runAsync 和 supplyAsync 用来将一个操作异步化。supplyAsync 可以指定线程池,若不指定,则使用一个公共的线程池(CompletableFuture 类中的一个静态私有变量(Executor))。

supplyAsync(supplier);
supplyAsync(supplier, executor);

应该根据实际场景,用合适的方式使用自己创建的线程池。例如,在 HTTP 客户端开发中,每个 Client 中应该包含一个线程池,避免过多请求拥挤在一个线程池中。

CompletableFuture 实现了 CompletionStage,而 CompletionStage 提供了 then 系列方法(如 thenCombine,thenCombine)可以将操作组成流程图,且支持条件执行和关系汇聚。

RxJava 不是 Java 8 才有的(Java 6 即可支持),功能极为强大。本文不是 RxJava 教程,此处从略。

根据使用场景,灵活选用二者。让 Java 异步编程轻松搞定。

结束语

上面介绍了 Java 8 主要新特性如何改善程序,其实 Java 8 还有很多新的细节值得进一步挖掘。现在 Java 11 早已发布,Java 17 也已经不远,新时代的 Java 不仅提供了便利的特性和工具,也能更好地适应现代地基础架构(如轻量级开发、容器化、Serverless),让产品持续焕发活力。

程序员灯塔
转载请注明原文链接:让 Java 8 改进我们的程序
喜欢 (0)