## Chapter 6. Enums and Annotations(枚舉和注解)
### Item 41: Use marker interfaces to define types(使用標記接口定義類型)
A marker interface is an interface that contains no method declarations but merely designates (or “marks”) a class that implements the interface as having some property. For example, consider the Serializable interface (Chapter 12). By implementing this interface, a class indicates that its instances can be written to an ObjectOutputStream (or “serialized”).
標記接口是一種不包含任何方法聲明的接口,它只是指定(或「標記」)一個類,該類實現了具有某些屬性的接口。例如,考慮 Serializable 接口(Chapter 12)。通過實現此接口,表示類的實例可以寫入 ObjectOutputStream(或「序列化」)。
You may hear it said that marker annotations (Item 39) make marker interfaces obsolete. This assertion is incorrect. Marker interfaces have two advantages over marker annotations. First and foremost, **marker interfaces define a type that is implemented by instances of the marked class; marker annotations do not.** The existence of a marker interface type allows you to catch errors at compile time that you couldn’t catch until runtime if you used a marker annotation.
你可能聽過一個說法:標記接口已經過時,更好的方式是標記注解([Item-39](/Chapter-6/Chapter-6-Item-39-Prefer-annotations-to-naming-patterns.md))。這個言論是錯誤的。與標記注解相比,標記接口有兩個優點。首先,**標記接口定義的類型由標記類的實例實現;標記注解不會。** 標記接口類型的存在允許你在編譯時捕獲錯誤,如果你使用標記注解,則在運行時才能捕獲這些錯誤。
Java’s serialization facility (Chapter 6) uses the Serializable marker interface to indicate that a type is serializable. The ObjectOutputStream.writeObject method, which serializes the object that is passed to it, requires that its argument be serializable. Had the argument of this method been of type Serializable, an attempt to serialize an inappropriate object would have been detected at compile time (by type checking). Compile-time error detection is the intent of marker interfaces, but unfortunately, the ObjectOutputStream.write API does not take advantage of the Serializable interface: its argument is declared to be of type Object, so attempts to serialize an unserializable object won’t fail until runtime.
Java 的序列化工具(Chapter 6)使用 Serializable 標記接口來表明一個類是可序列化的。`ObjectOutputStream.writeObject` 方法序列化傳遞給它的對象,它要求其參數是可序列化的。假設該方法的參數類型是 Serializable,那么在編譯時(通過類型檢查)就會檢測到對不合適的對象進行序列化的錯誤。編譯時錯誤檢測是使用標記接口的目的,但不幸的是,`ObjectOutputStream.writeObject` 沒有利用 Serializable 接口:它的參數被聲明為 Object 類型,因此,如果嘗試序列化一個不可序列化對象,直到運行時才會提示失敗。
**譯注 1:原文 `ObjectOutputStream.write` 有誤,該方法的每種重載僅支持 int 類型和 byte[],應修改為 `ObjectOutputStream.writeObject`,其源碼如下:**
```
public final void writeObject(Object obj) throws IOException {
if (enableOverride) {
writeObjectOverride(obj);
return;
}
try {
writeObject0(obj, false);
} catch (IOException ex) {
if (depth == 0) {
writeFatalException(ex);
}
throw ex;
}
}
```
**譯注 2:使用 ObjectOutputStream.writeObject 的例子**
```
public class BaseClass implements Serializable {
private final int id;
private final String name;
public BaseClass(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "id=" + id + ", name='" + name + '\'';
}
}
public class Main {
private void Out() throws IOException {
BaseClass obj = new BaseClass(1, "Mark");
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(new File("out.txt")))) {
out.writeObject(obj);
}
}
private void In() throws IOException, ClassNotFoundException {
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(new File("out.txt")))) {
BaseClass obj = (BaseClass) in.readObject();
System.out.println(obj);
}
}
}
```
**Another advantage of marker interfaces over marker annotations is that they can be targeted more precisely.** If an annotation type is declared with target ElementType.TYPE, it can be applied to any class or interface. Suppose you have a marker that is applicable only to implementations of a particular interface. If you define it as a marker interface, you can have it extend the sole interface to which it is applicable, guaranteeing that all marked types are also subtypes of the sole interface to which it is applicable.
**標記接口相對于標記注解的另一個優點是可以更精確地定位它們。** 如果注解類型使用 `@Target(ElementType.TYPE)` 聲明,它可以應用于任何類或接口。假設你有一個只適用于特定接口來實現的標記。如果將其定義為標記接口,則可以讓它擴展其適用的惟一接口,確保所有標記的類型也是其適用的惟一接口的子類型。
Arguably, the Set interface is just such a restricted marker interface. It is applicable only to Collection subtypes, but it adds no methods beyond those defined by Collection. It is not generally considered to be a marker interface because it refines the contracts of several Collection methods, including add, equals, and hashCode. But it is easy to imagine a marker interface that is applicable only to subtypes of some particular interface and does not refine the contracts of any of the interface’s methods. Such a marker interface might describe some invariant of the entire object or indicate that instances are eligible for processing by a method of some other class (in the way that the Serializable interface indicates that instances are eligible for processing by ObjectOutputStream).
可以說,Set 接口就是這樣一個受限的標記接口。它只適用于 Collection 的子類,但是除了 Collection 定義的方法之外,它不添加任何方法。它通常不被認為是一個標記接口,因為它細化了幾個 Collection 方法的約定,包括 add、equals 和 hashCode。但是很容易想象一個標記接口只適用于某些特定接口的子類,而不細化任何接口方法的約定。這樣的標記接口可能描述整個對象的某個不變量,或者表明實例能夠利用其他類的方法進行處理(就像 Serializable 接口能夠利用 ObjectOutputStream 進行處理一樣)。
**The chief advantage of marker annotations over marker interfaces is that they are part of the larger annotation facility.** Therefore, marker annotations allow for consistency in annotation-based frameworks.
**相對于標記接口,標記注解的主要優勢是它們可以是其他注解功能的一部分。** 因此,標記注解能夠與基于使用注解的框架保持一致性。
So when should you use a marker annotation and when should you use a marker interface? Clearly you must use an annotation if the marker applies to any program element other than a class or interface, because only classes and interfaces can be made to implement or extend an interface. If the marker applies only to classes and interfaces, ask yourself the question “Might I want to write one or more methods that accept only objects that have this marking?” If so, you should use a marker interface in preference to an annotation. This will make it possible for you to use the interface as a parameter type for the methods in question, which will result in the benefit of compile-time type checking. If you can convince yourself that you’ll never want to write a method that accepts only objects with the marking, then you’re probably better off using a marker annotation. If, additionally, the marking is part of a framework that makes heavy use of annotations, then a marker annotation is the clear choice.
那么什么時候應該使用標記注解,什么時候應該使用標記接口呢?顯然,如果標記應用于類或接口之外的任何程序元素,則必須使用標記注解,因為只有類和接口才能實現或擴展接口。如果標記只適用于類和接口,那么可以問自己這樣一個問題:「我是否可以編寫一個或多個方法,只接受具有這種標記的對象?」如果是這樣,你應該使用標記接口而不是標記注解。這將使你能夠將接口用作相關方法的參數類型,這將帶來編譯時類型檢查的好處。如果你確信自己永遠不會編寫只接受帶有標記的對象的方法,那么最好使用標記注解。此外,如果框架大量使用注解,那么標記注解就是明確的選擇。
In summary, marker interfaces and marker annotations both have their uses. If you want to define a type that does not have any new methods associated with it, a marker interface is the way to go. If you want to mark program elements other than classes and interfaces or to fit the marker into a framework that already makes heavy use of annotation types, then a marker annotation is the correct choice. **If you find yourself writing a marker annotation type whose target is ElementType.TYPE, take the time to figure out whether it really should be an annotation type or whether a marker interface would be more appropriate.**
總之,標記接口和標記注解都有各自的用途。如果你想要定義一個沒有與之關聯的新方法的類型,可以使用標記接口。如果你希望標記類和接口之外的程序元素,或者將標記符放入已經大量使用注解類型的框架中,那么標記注解就是正確的選擇。如果你發現自己編寫的標記注解類型有 `@Target(ElementType.TYPE)` 聲明(**譯注:意在說明既可以用標記注解,也可以用標記接口的情況**),那么請花時間弄清楚究竟應該用注解類型,還是標記接口更合適。
In a sense, this item is the inverse of Item 22, which says, “If you don’t want to define a type, don’t use an interface.” To a first approximation, this item says, “If you do want to define a type, do use an interface.”
從某種意義上說,本條目與 [Item-22](/Chapter-4/Chapter-4-Item-22-Use-interfaces-only-to-define-types.md) 的說法相反,也就是說,「如果不想定義類型,就不要使用接口。」,與本條目應用場景適應的說法是,「如果你確實想定義類型,那么就要使用接口。」
---
**[Back to contents of the chapter(返回章節目錄)](/Chapter-6/Chapter-6-Introduction.md)**
- **Previous Item(上一條目):[Item 40: Consistently use the Override annotation(堅持使用 @Override 注解)](/Chapter-6/Chapter-6-Item-40-Consistently-use-the-Override-annotation.md)**
- **Next Item(下一條目):[Chapter 7 Introduction(章節介紹)](/Chapter-7/Chapter-7-Introduction.md)**
- Chapter 2. Creating and Destroying Objects(創建和銷毀對象)
- Item 1: Consider static factory methods instead of constructors(考慮以靜態工廠方法代替構造函數)
- Item 2: Consider a builder when faced with many constructor parameters(在面對多個構造函數參數時,請考慮構建器)
- Item 3: Enforce the singleton property with a private constructor or an enum type(使用私有構造函數或枚舉類型實施單例屬性)
- Item 4: Enforce noninstantiability with a private constructor(用私有構造函數實施不可實例化)
- Item 5: Prefer dependency injection to hardwiring resources(依賴注入優于硬連接資源)
- Item 6: Avoid creating unnecessary objects(避免創建不必要的對象)
- Item 7: Eliminate obsolete object references(排除過時的對象引用)
- Item 8: Avoid finalizers and cleaners(避免使用終結器和清除器)
- Item 9: Prefer try with resources to try finally(使用 try-with-resources 優于 try-finally)
- Chapter 3. Methods Common to All Objects(對象的通用方法)
- Item 10: Obey the general contract when overriding equals(覆蓋 equals 方法時應遵守的約定)
- Item 11: Always override hashCode when you override equals(當覆蓋 equals 方法時,總要覆蓋 hashCode 方法)
- Item 12: Always override toString(始終覆蓋 toString 方法)
- Item 13: Override clone judiciously(明智地覆蓋 clone 方法)
- Item 14: Consider implementing Comparable(考慮實現 Comparable 接口)
- Chapter 4. Classes and Interfaces(類和接口)
- Item 15: Minimize the accessibility of classes and members(盡量減少類和成員的可訪問性)
- Item 16: In public classes use accessor methods not public fields(在公共類中,使用訪問器方法,而不是公共字段)
- Item 17: Minimize mutability(減少可變性)
- Item 18: Favor composition over inheritance(優先選擇復合而不是繼承)
- Item 19: Design and document for inheritance or else prohibit it(繼承要設計良好并且具有文檔,否則禁止使用)
- Item 20: Prefer interfaces to abstract classes(接口優于抽象類)
- Item 21: Design interfaces for posterity(為后代設計接口)
- Item 22: Use interfaces only to define types(接口只用于定義類型)
- Item 23: Prefer class hierarchies to tagged classes(類層次結構優于帶標簽的類)
- Item 24: Favor static member classes over nonstatic(靜態成員類優于非靜態成員類)
- Item 25: Limit source files to a single top level class(源文件僅限有單個頂層類)
- Chapter 5. Generics(泛型)
- Item 26: Do not use raw types(不要使用原始類型)
- Item 27: Eliminate unchecked warnings(消除 unchecked 警告)
- Item 28: Prefer lists to arrays(list 優于數組)
- Item 29: Favor generic types(優先使用泛型)
- Item 30: Favor generic methods(優先使用泛型方法)
- Item 31: Use bounded wildcards to increase API flexibility(使用有界通配符增加 API 的靈活性)
- Item 32: Combine generics and varargs judiciously(明智地合用泛型和可變參數)
- Item 33: Consider typesafe heterogeneous containers(考慮類型安全的異構容器)
- Chapter 6. Enums and Annotations(枚舉和注解)
- Item 34: Use enums instead of int constants(用枚舉類型代替 int 常量)
- Item 35: Use instance fields instead of ordinals(使用實例字段替代序數)
- Item 36: Use EnumSet instead of bit fields(用 EnumSet 替代位字段)
- Item 37: Use EnumMap instead of ordinal indexing(使用 EnumMap 替換序數索引)
- Item 38: Emulate extensible enums with interfaces(使用接口模擬可擴展枚舉)
- Item 39: Prefer annotations to naming patterns(注解優于命名模式)
- Item 40: Consistently use the Override annotation(堅持使用 @Override 注解)
- Item 41: Use marker interfaces to define types(使用標記接口定義類型)
- Chapter 7. Lambdas and Streams(λ 表達式和流)
- Item 42: Prefer lambdas to anonymous classes(λ 表達式優于匿名類)
- Item 43: Prefer method references to lambdas(方法引用優于 λ 表達式)
- Item 44: Favor the use of standard functional interfaces(優先使用標準函數式接口)
- Item 45: Use streams judiciously(明智地使用流)
- Item 46: Prefer side effect free functions in streams(在流中使用無副作用的函數)
- Item 47: Prefer Collection to Stream as a return type(優先選擇 Collection 而不是流作為返回類型)
- Item 48: Use caution when making streams parallel(謹慎使用并行流)
- Chapter 8. Methods(方法)
- Item 49: Check parameters for validity(檢查參數的有效性)
- Item 50: Make defensive copies when needed(在需要時制作防御性副本)
- Item 51: Design method signatures carefully(仔細設計方法簽名)
- Item 52: Use overloading judiciously(明智地使用重載)
- Item 53: Use varargs judiciously(明智地使用可變參數)
- Item 54: Return empty collections or arrays, not nulls(返回空集合或數組,而不是 null)
- Item 55: Return optionals judiciously(明智地的返回 Optional)
- Item 56: Write doc comments for all exposed API elements(為所有公開的 API 元素編寫文檔注釋)
- Chapter 9. General Programming(通用程序設計)
- Item 57: Minimize the scope of local variables(將局部變量的作用域最小化)
- Item 58: Prefer for-each loops to traditional for loops(for-each 循環優于傳統的 for 循環)
- Item 59: Know and use the libraries(了解并使用庫)
- Item 60: Avoid float and double if exact answers are required(若需要精確答案就應避免使用 float 和 double 類型)
- Item 61: Prefer primitive types to boxed primitives(基本數據類型優于包裝類)
- Item 62: Avoid strings where other types are more appropriate(其他類型更合適時應避免使用字符串)
- Item 63: Beware the performance of string concatenation(當心字符串連接引起的性能問題)
- Item 64: Refer to objects by their interfaces(通過接口引用對象)
- Item 65: Prefer interfaces to reflection(接口優于反射)
- Item 66: Use native methods judiciously(明智地使用本地方法)
- Item 67: Optimize judiciously(明智地進行優化)
- Item 68: Adhere to generally accepted naming conventions(遵守被廣泛認可的命名約定)
- Chapter 10. Exceptions(異常)
- Item 69: Use exceptions only for exceptional conditions(僅在確有異常條件下使用異常)
- Item 70: Use checked exceptions for recoverable conditions and runtime exceptions for programming errors(對可恢復情況使用 checked 異常,對編程錯誤使用運行時異常)
- Item 71: Avoid unnecessary use of checked exceptions(避免不必要地使用 checked 異常)
- Item 72: Favor the use of standard exceptions(鼓勵復用標準異常)
- Item 73: Throw exceptions appropriate to the abstraction(拋出能用抽象解釋的異常)
- Item 74: Document all exceptions thrown by each method(為每個方法記錄會拋出的所有異常)
- Item 75: Include failure capture information in detail messages(異常詳細消息中應包含捕獲失敗的信息)
- Item 76: Strive for failure atomicity(盡力保證故障原子性)
- Item 77: Don’t ignore exceptions(不要忽略異常)
- Chapter 11. Concurrency(并發)
- Item 78: Synchronize access to shared mutable data(對共享可變數據的同步訪問)
- Item 79: Avoid excessive synchronization(避免過度同步)
- Item 80: Prefer executors, tasks, and streams to threads(Executor、task、流優于直接使用線程)
- Item 81: Prefer concurrency utilities to wait and notify(并發實用工具優于 wait 和 notify)
- Item 82: Document thread safety(文檔應包含線程安全屬性)
- Item 83: Use lazy initialization judiciously(明智地使用延遲初始化)
- Item 84: Don’t depend on the thread scheduler(不要依賴線程調度器)
- Chapter 12. Serialization(序列化)
- Item 85: Prefer alternatives to Java serialization(優先選擇 Java 序列化的替代方案)
- Item 86: Implement Serializable with great caution(非常謹慎地實現 Serializable)
- Item 87: Consider using a custom serialized form(考慮使用自定義序列化形式)
- Item 88: Write readObject methods defensively(防御性地編寫 readObject 方法)
- Item 89: For instance control, prefer enum types to readResolve(對于實例控制,枚舉類型優于 readResolve)
- Item 90: Consider serialization proxies instead of serialized instances(考慮以序列化代理代替序列化實例)