Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FLINK-13681] Translate "Code Style - Java Guide" page into Chinese #279

Open
wants to merge 1 commit into
base: asf-site
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 54 additions & 57 deletions contributing/code-style-and-quality-java.zh.md
Original file line number Diff line number Diff line change
@@ -1,113 +1,110 @@
---
title: "Apache Flink Code Style and Quality Guide — Java"
title: "Apache Flink 代码风格和质量指南 — Java"
---

{% include code-style-navbar.zh.md %}

{% toc %}


## Java Language Features and Libraries
## Java 语言的特点和类库


### Preconditions and Log Statements
### 前提条件判断和日志格式

* Never concatenate strings in the parameters
* <span style="text-decoration:underline;">Don’t:</span> `Preconditions.checkState(value <= threshold, "value must be below " + threshold)`
* <span style="text-decoration:underline;">Don’t:</span> `LOG.debug("value is " + value)`
* <span style="text-decoration:underline;">Do:</span> `Preconditions.checkState(value <= threshold, "value must be below %s", threshold)`
* <span style="text-decoration:underline;">Do:</span> `LOG.debug("value is {}", value)`
* 不要在参数中拼接字符串
* <span style="text-decoration:underline;">不要:</span> `Preconditions.checkState(value <= threshold, "value must be below " + threshold)`
* <span style="text-decoration:underline;">不要:</span> `LOG.debug("value is " + value)`
* <span style="text-decoration:underline;">可以:</span> `Preconditions.checkState(value <= threshold, "value must be below %s", threshold)`
* <span style="text-decoration:underline;">可以:</span> `LOG.debug("value is {}", value)`


### Generics
### 泛型

* **No raw types:** Do not use raw types, unless strictly necessary (sometimes necessary for signature matches, arrays).
* **Suppress warnings for unchecked conversions:** Add annotations to suppress warnings, if they cannot be avoided (such as “unchecked”, or “serial”). Otherwise warnings about generics flood the build and drown relevant warnings.
* **不要使用原始类型:** 除非必要场景(例如在一些方法签名和数组类型情况下),不要使用原始类型。
* **抑制非检转换类警告:** 增加注释来抑制那些无法避免的警告,例如没有进行类型检查或者没有申明`serialVersionUID`。否则的话,这些关于泛型的警告会在编译时大量涌现。

### equals() / hashCode() 方法

### equals() / hashCode()
* **只有在明确定义的情况下,才实现 equals() / hashCode() 方法.**
* 在没有明确定义的情况下,**只是为了在测试中进行简单判定时,无需实现这些方法**。在这种场景下请使用`hamcrest matchers`: [https://github.com/junit-team/junit4/wiki/matchers-and-assertthat](https://github.com/junit-team/junit4/wiki/matchers-and-assertthat)
* 这些方法没有被明确定义的一个常见指标是,它们只考虑了类字段的部分子集(除了那些纯辅助的字段之外)。
* 当这些方法将可变字段考虑在内时,往往会遇到设计上的问题,因为`equals()`/`hashCode()`方法表明可以将该类作为散列的键,但是签名本身又表明改变类本身是安全的。

* **equals() / hashCode() should be added when they are well defined only.**
* They should **not be added to enable a simpler assertion in tests** when they are not well defined. Use hamcrest matchers in that case: [https://github.com/junit-team/junit4/wiki/matchers-and-assertthat](https://github.com/junit-team/junit4/wiki/matchers-and-assertthat)
* A common indicator that the methods are not well defined is when they take a subset of the fields into account (other than fields that are purely auxiliary).
* When the methods take mutable fields into account, you often have a design issue. The `equals()`/`hashCode()` methods suggest to use the type as a key, but the signatures suggest it is safe to keep mutating the type.

### Java 序列化

### Java Serialization

* **Do not use Java Serialization for anything !!!**
* **Do not use Java Serialization for anything !!! !!!**
* **Do not use Java Serialization for anything !!! !!! !!!**
* Internal to Flink, Java serialization is used to transport messages and programs through RPC. This is the only case where we use Java serialization. Because of that, some classes need to be serializable (if they are transported via RPC).
* **Serializable classes must define a Serial Version UID:**
* **不要使用Java序列化 !!!**
* **不要使用Java序列化 !!! !!!**
* **不要使用Java序列化 !!! !!! !!!**
* 在Flink内部,Java序列化被用于通过RPC传输消息和程序。这是使用Java序列化的唯一情况。 因此,某些使用RPC传输的类需要可序列化。
* **可序列化类必须定义一个Serial Version UID:**

`private static final long serialVersionUID = 1L;`
* **The Serial Version UID for new classes should start at 1** and should generally be bumped on every incompatible change to the class according to the Java serialization compatibility definition (i.e: changing the type of a field, or moving the position of a class in the class hierarchy).
* **对于新的类,Serial Version UID应该从1开始** ,而且通常应该根据Java序列化兼容性定义,随着每次类的不兼容改动而修改(例如:更改字段的类型或者在类层次结构中移动类的位置).


### Java Reflection
### Java 反射

**Avoid using Java’s Reflection API**
**尽量避免使用Java的反射API**

* Java’s Reflection API can be a very useful tool in certain cases but in all cases it is a hack and one should research for alternatives. The only cases where Flink should use reflection are
* Dynamically loading implementations from another module (like webUI, additional serializers, pluggable query processors).
* Extracting types inside the TypeExtractor class. This is fragile enough and should not be done outside the TypeExtractor class.
* Some cases of cross-JDK version features, where we need to use reflection because we cannot assume a class/method to be present in all versions.
* If you need reflection for accessing methods or fields in tests, it usually indicates some deeper architectural issues, like wrong scoping, bad separation of concerns, or that there is no clean way to provide components / dependencies to the class that is tested
* Java的反射功能在某些特定场景下可能是一个非常有用的工具,但无论如何这都是一种很骇客的做法,应该尽量使用其替代方法。Flink只有在如下场景才能使用Java的反射方法:
* 动态加载其他模块的实现 (例如webUI,额外的序列化器,插件化的查询处理器).
* 在TypeExtractor类里提取出类型. 因为这个操作往往是很脆弱的,所以不应该在TypeExtractor类外进行操作。
* 在某些跨JDK版本的功能上,由于无法假定所有版本中都存在此类/方法,因此我们需要使用反射。
* 如果需要使用反射才能访问测试中的方法或者字段,则表明实际上存在一些深层次的体系结构问题,例如错误的作用域,不恰当的关系分离,或者没有干净的方法为测试类提供组件/依赖项。


### Collections
### 集合类

* **ArrayList and ArrayDeque are almost always superior to LinkedList**, except when frequently insert and deleting in the middle of the list
* **For Maps, avoid patterns that require multiple lookups**
* `contains()` before `get()` → `get()` and check null
* `contains()` before `put()` → `putIfAbsent()` or `computeIfAbsent()`
* Iterating over keys, getting values → iterate over `entrySet()`
* **Set the initial capacity for a collection only if there is a good proven reason** for that, otherwise do not clutter the code. In case of **Maps** it can be even deluding because the Map's load factor effectively reduces the capacity.
* **ArrayList和ArrayDeque几乎总是优先于LinkedList使用**, 除非总是在列表中间位置进行频繁地插入和删除时。
* **对于映射, 需要避免多次查询的访问模式**
* 在`get()`之前调用`contains()` → 直接调用`get()` 然后检查返回结果是否为null
* 在`put()`之前调用`contains()` → 使用`putIfAbsent()` 或者 `computeIfAbsent()`
* 遍历键,然后获取对应的值 → 使用`entrySet()`进行遍历。
* **仅在有充分的理由的情况下,才设置集合的初始容量** ,以保持代码的整洁。对于 **映射** 来说,因为负载系数往往会更有效地降低容量,所以其初始容量的设置可以说是具有欺骗性的。


### Java Optional

* Use **@Nullable annotation where you do not use Optional** for the nullable values.
* If you can prove that `Optional` usage would lead to a **performance degradation in critical code then fallback to @Nullable**.
* Always use **Optional to return nullable values** in the API/public methods except the case of a proven performance concern.
* **Do not use Optional as a function argument**, instead either overload the method or use the Builder pattern for the set of function arguments.
* Note: an Optional argument can be allowed in a private helper method if you believe that it simplifies the code
([example](https://github.com/apache/flink/blob/master/flink-formats/flink-avro/src/main/java/org/apache/flink/formats/avro/typeutils/AvroFactory.java#L95)).
* **Do not use Optional for class fields**.
* **当你不使用Optional时,请对那些可能会是null的变量使用@Nullable注释**
* 如果你能证明`Optional`用法会导致 **在关键代码上的性能退化,可以退到使用 @Nullable**.
* 总是在API/公开方法里 **使用Optional来返回可能是null的值**,除非那些被证明是有性能问题的场景。
* **不要在函数参数中使用Optional**, 而是使用重载方法或者对函数参数使用Builder模式。
* 注意: 如果您认为Optional参数可以简化代码,则可以在私有方法中使用此参数
([例子](https://github.com/apache/flink/blob/master/flink-formats/flink-avro/src/main/java/org/apache/flink/formats/avro/typeutils/AvroFactory.java#L95)).
* **不要对类字段使用Optional**.


### Lambdas

* Prefer non-capturing lambdas (lambdas that do not contain references to the outer scope). Capturing lambdas need to create a new object instance for every call. Non-capturing lambdas can use the same instance for each invocation.
* 首选非捕获型lambda(即不包含对外部作用域的引用)。捕获型lambda需要为每个调用创建一个新的对象实例,而非捕获的lambda可以为每个调用使用相同的实例。

**don’t:**
**不要:**
```
map.computeIfAbsent(key, x -> key.toLowerCase())
```

**do:**
**可以:**
```
map.computeIfAbsent(key, k -> k.toLowerCase());
```

* Consider method references instead of inline lambdas
* 考虑优先使用方法引用而不是内联lambda

**don’t**:
**不要**:
```
map.computeIfAbsent(key, k-> Loader.load(k));
```
**do:**

**可以:**
```
map.computeIfAbsent(key, Loader::load);
```


### Java Streams

* Avoid Java Streams in any performance critical code.
* The main motivation to use Java Streams would be to improve code readability. As such, they can be a good match in parts of the code that are not data-intensive, but deal with coordination..
* Even in the latter case, try to limit the scope to a method, or a few private methods within an internal class.


* 避免在任何性能关键代码中使用Java Streams。
* 使用Java Streams的主要动机是为了提高代码的可读性。它们可以很好地应用在那些不是数据密集型,但是需要协调处理的代码中。
* 即使在后一种情况下,也应尝试将使用范围限制在一个方法或内部类中的一些私有方法中。