在Java 11时代的Java编程风格指南
Java的发布周期已经改变,截至2019年1月,最新版本是Java 11。在语法方面也有所改进,现在可以看到现代Java的风格正在发生变化。本文将总结Java 11时代现代化的Java编程风格。
由于本文包含笔者的主观观点,请您注意这一点。
var(本地变量类型推断)
var を積極的に利用する。
在Java 10中引入了var来声明局部变量的类型,但基本上可以积极地在可使用的场景中使用它。许多现代语言都采用了类似的类型推断,但从未听说过由此引起的问题。
对于经过高度培训的Java程序员来说,一边型定义等IDE会自动补全,所以对于觉得不方便的意见有一定道理,但仅在阅读代码时,Java左边的字符数量过多导致可读性严重降低,显得冗长。
// before
Owner owner = new Owner();
List<Pet> pets = List.of(new Pet("pochi"), new Pet("tama"));
// after
var owner = new Owner();
var pets = List.of(new Pet("pochi"), new Pet("tama"));
在上述的例子中并没有什么大不了的问题,但是考虑到包括泛型在内,Java类名往往会变得很长,所以在许多情况下使用”var”关键字可以减少信息量,使得代码更易读。
集合操作
不可修改的 (bù kě xiū de)
- コレクションを変更不可能にできる箇所ではできるだけ変更不可能にする。
在Java的标准集合库中,没有名为ImmutableList的表示不可变性的接口,但是可以通过在尝试修改元素时引发异常来实现阻止变更的功能。为了防止意外更改元素,最好将可以变更不可的部分尽可能地设置为不可变。
// Java 9 で追加されたファクトリーメソッドは変更不可能なコレクションを生成する
List.of(1, 2, 3); // unmodifiable
Set.of(1, 2, 3); // unmodifiable
Map.of("keyA", "valueA", "keyB", "ValueB"); // unmodifiable
// Collections クラスにはコレクションの変更不可能なビューを返すメソッドが用意されている。
Collections.unmodifiableList(modifiableList); // unmodifiable
// Java 10 で追加されたファクトリーメソッドによっても既存のコレクションの変更不可能なビューを作成可能
List.copyOf(modifiableList); // unmodifiable
// Java 10 では Stream の Collector として変更不可能なコレクションに変換するメソッドが追加されている
modifiableList.stream().collect(Collectors.toUnmodifiableList());
流API
- 極力 for 文を使用せず、Stream API によって処理する。
由于 Java 8 引入了 Stream API,for 循环的使用机会几乎消失了。 Stream API 的声明性描述使得代码可读性高,而且从性能角度来看也更优秀,因此除了一些特殊情况,最好使用 Stream API 来进行集合操作。
var beforeList = List.of("a", "b", "c");
// before
var afterList1 = new ArrayList<String>();
for (var str : beforeList) {
afterList.add(str.toUpperCase());
}
// after
var afterList2 = beforeList
.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
空
请提供以下的一个选项的中文本地化的释义。
- メソッドの戻り値として null が返り得る場合は、Optional 型として返す。
在Java 8中,添加了Optional类型来表示可能为null的值。没有理由不使用它,所以在可能返回null的情况下,而不是直接返回null,应始终将其作为Optional类型返回。
Optional<String> getName() {
return Optional.ofNullable(this.name);
}
由于Optional类型似乎主要用作方法返回值,所以最好在方法的参数或字段中基本上不使用它。
可为空 (Kě .
null 可否をアノテーションによって表現する。
Kotlin的优势之一经常被提及的是它的空安全性,但是在Java中,也有一种部分实现这一机制的机制,即通过注解声明可能为空。尽管该机制没有作为Java的标准功能被采用,但它是一种被广泛使用的机制,需要引入某些第三方库来实现。
如果使用Spring Framework 5提供的注解,则可以实现以下表达方式。
public @Nullable Pet getPetByName(@NonNull String name) {
// 引数 name には null を渡してはならない
// 返り値 Pet は null になりうる(通常は Optional<Pet> とするほうがよい)
}
顺便提一下,在使用 Kotlin 调用 Java 代码时,这些注解也会被应用,可以识别为非空类型,这是一个好处。
- Nullability annotations – Calling Java from Kotlin – Kotlin Programming Language
※具体使用哪个注解库,现在无法明确断言。我认为目前被最广泛使用的可能是JSR 305,但是这个JSR处于停滞状态,并且未来也没有採用的计划,从许可证的角度来看,也不推荐使用。我认为Checker Framework可能是一个有力的选择,但还有其他选择,因此需要进行多方比较来决定使用哪个。
参考链接:
– nullpointerexception – 在Java中应该使用哪个@NotNull注解? – Stack Overflow
– https://qiita.com/ptiringo/items/71c2792e7c553399c2e2#comment-b7026b640ba564ba7b2d
检查对象是否为 null
- メソッドの null 非許容な引数は Objects.requireNonNull メソッドを使って早いタイミングで null チェックを行う。
使用表示空值能力的注释可以在一定程度上防止空值的混入,但它只是静态分析,在运行时无能为力。为了早期检测运行时的空值混入,最终还是需要在方法内部进行空值检查。
使用Java 7引入的Objects.requireNonNull方法可以简洁地进行空值检查,建议积极使用此方法。
Pet(@NonNull String name, @NonNull LocalDate birthdate) {
this.name = Objects.requireNonNull(name);
this.birthdate = Objects.requireNonNull(birthdate);
}
自动资源管理
- リソースのクローズ処理が必要な箇所では常に try-with-resources を使う。
Java 7 推出了 try-with-resource 机制,使得资源的关闭处理变得更加容易。对于像数据库连接等无论成功还是失败都必须进行关闭处理的资源,建议始终使用 try-with-resource 机制来进行关闭处理。
// before
FileInputStream inputStream1 = null;
try {
inputStream1 = new FileInputStream("foo.txt");
inputStream1.read();
} finally {
if (inputStream1 != null) {
try {
inputStream1.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
// after
try (var inputStream2 = Files.newInputStream(Path.of("foo.txt"))) {
inputStream2.read();
}
日期与时间
-
- Date and Time API を使う。
- Date クラスや Calendar クラス、SimpleDateFormat クラスなどの旧クラス群は特別な理由がない限り使用しない。
在Java 8中引入的日期和时间API使得几乎可以全部替代旧的Date类、Calendar类和SimpleDateFormatter类。除非有特殊的理由,最好不要使用旧的类。
// before
var date = new Date();
var simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd");
System.out.println(simpleDateFormat.format(date));
// after
var localDate = LocalDate.now();
var dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
System.out.println(dateTimeFormatter.format(localDate));
可以说,像Joda-Time这样的库已经发挥了作用,以解决旧有类群的使用不便问题。
- Date and Time API 復習 – Qiita
文件操作
java.io パッケージのクラス (Java I/O) よりも java.nio.file パッケージのクラス (NIO2) を優先的に使用する。
Java 7 引入的 NIO2 已经让 Java 的文件操作 API 变得更新。基本上,最好遵循使用新的 API 而尽量不使用旧的 API 的方法。
// before
var dir = new File("/tmp/dir");
var succeeded = dir.mkdir();
if (succeeded) {
try {
new File(dir, "foo.txt").createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
// after
try {
var directory = Files.createDirectory(Path.of("/tmp/dir2"));
Files.createFile(directory.resolve("foo.txt"));
} catch (IOException e) {
e.printStackTrace();
}
尽量使用 Path.of(..) 和 Files.xxx,而不要使用 new File(..)。
-
- Java NIO2 のおさらいメモ – Qiita
- java.io.File のコードを java.nio.Path と java.nio.Files を使って書き直す – Qiita
模块
按照理解的程度进行补充记录……。