JDK 8 之后都添加了什么新玩意

2022-09-12, 星期一, 15:57

JavaDev

本文写作时在 JDK 8 之后有 11 和 17 两个 LTS 版本。由于 JDK 9 不是 LTS 版本,该版本中引入的不可变集合实例工厂方法划入 JDK 11 章节,其他编排同理。

JDK 11

不可变集合实例的工厂方法

List<Integer> list = List.of(1, 2, 3);
Map<String, String> map = Map.of(key1, value1, key2, value2);

Optional::orElseThrowOptional::ifPresentOrElse

userRepository.findById(userId) 查询得到 user: Optional<User>,原有写法可以是:

if (user.isPersent()) {
    return user.get();
} else {
    throw new NoSuchElementException();
}

return user.orElseThrow() 可以在 user 不存在时直接抛出 NoSuchElementException。当然抛出其他异常也是可以的 return user.orElseThrow(IllegalArgumentException::new)

Optional::ifPresentOrElse 则接受 Consumer<T> actionRunnable emptyAction 两个参数,如果 value 存在,执行 action,不存在则执行 emptyAction,不过这样的话 user 就被消费掉了。

user.ifPresentOrElse(
    (v) -> logUserAction(v),
    () -> logNoSuchUserException();
);

var 类型推断

通过 var 省略局部变量的类型声明,也可以在 Lambda 表达式中使用。

var roleMap: Map<UserRole, List<Long>> = roleService.getRoleMapByUserId(userId);

java.net.http 中的标准化 HttpClient API

  • Builder 模式构造请求
  • 支持异步
  • Push Promises in HTTP/2
  • ……

主要分 java.net.http.HttpClientjava.net.http.HttpRequestjava.net.http.HttpResponse 三部分,具体使用可查看参考资料。

接口中允许私有方法

可以在 Interface 中添加 private 方法和 private 静态方法:

  • 接口中 private 方法只能由接口内部的方法调用
  • 接口中 private 静态方法可以在其他静态和非静态接口方法中使用

Try-with-resources allows effectively final variables

BufferedReader br1 = new BufferedReader(...);
BufferedReader br2 = new BufferedReader(...);
try (br1; br2) {
    System.out.println(br1.readLine() + br2.readLine());
}
// 需要注意 br1 和 br2 在这里已经被释放了,不要尝试使用

一些过于简单或有待深入讨论需要将来添加例子的内容

  • Reactive Streams publish-subscribe framework for asynchronous stream processing with non-blocking back pressure
  • Time-based enhancements to CompletableFuture (timeout, delay)
  • More options to transform (dropWhiletakeWhile) and generate (iterateofNullable) streams; readonly collectors (toUnmodifiableList); optionals can be transformed to streams
  • Arrays.mismatch: find the first mismatching element between two arrays

JDK 17

switch 表达式

如果 switch-case 中的分支只需要执行较简单的任务并返回一个值

switch(day) {
    case MONDAY, TUESDAY ... FRIDAY -> "workday";
    case SATURDAY, SUNDAY -> "weekend";
    default -> "invalid";
}

模式匹配

节省了强制类型转换,并且转换后的子类型可以当即参与条件判断。

使用 switch-case 结构:

switch(vehicle) {
    case Car c -> // return something
    case Truck t && t.wheels > 8 -> // return something else
    default -> throw new IllegalArgumentException("Not a known vehicle type");
}

使用 instanceof

if (vehicle instanceof Car c) {
    c.addPassenger(new Passenger());
} else if (vehicle instanceof Truck t && t.getNumberOfWheels() > 6) {
    t.loadCargo(new Cargo());
} else {
    throw new IllegalArgumentException("Not a known vehicle type");
}

Stream::toList

List<UserVO> userList = Stream.of().collect(Collectors.toList()):
// 简化为
List<UserVO> userList = Stream.of().toList();

需注意 Stream::toList 返回 ImmutableList

文本块

String blockOfText = """
First line
Second line
""";

record class

用于表示不可修改的纯数据类,在大部分场景下可以代替 DTOVO

// Point 类型表示二维平面上的一个点
public record Point(int x, int y) {}

该场景使用 Class 则需要实现(包括使用 Lombok)构造函数、getters、toStringequalshashCode

更详细的 NullPointerException

Wheel wheel = city.getParkingLot(0).getCar(0).getWheel(0);
// 现在 NullPointerException 可以携带是哪一级出了问题的信息

密封类 Sealed Class

可以限制其他类扩展,在模式匹配中就不需要指定 default 情况了。

public abstract sealed class Shape permits Circle, Rectangle {...}

public final class Circle extends Shape {...} // OK
public final class Triangle extends Shape {...} // Compile error

如果需要给枚举添加更多逻辑,密封类也是一个较好的选择,可以使用 getPermittedSubclasses获取子类。

一些过于简单或有待深入讨论需要将来添加例子的内容

  • Stream.mapMulti to replace each element of this stream with zero or more elements, an alternative to flatMap
  • Collectors.teeing to create a Collector that is a composite of two downstream collectors
  • @Serial to indicate fields and methods that are part of the serialization mechanism (e.g. serialVersionUID and readObject)
  • jlink is a tool that generates a custom Java runtime image that contains only the platform modules that are required for a given application
  • jpackage is a command-line tool to create native installers and packages for Java applications