diff --git a/Chapter-10-Introduction.md b/Chapter-10/Chapter-10-Introduction.md similarity index 100% rename from Chapter-10-Introduction.md rename to Chapter-10/Chapter-10-Introduction.md diff --git a/Chapter-10-Item-69-Use-exceptions-only-for-exceptional-conditions.md b/Chapter-10/Chapter-10-Item-69-Use-exceptions-only-for-exceptional-conditions.md similarity index 100% rename from Chapter-10-Item-69-Use-exceptions-only-for-exceptional-conditions.md rename to Chapter-10/Chapter-10-Item-69-Use-exceptions-only-for-exceptional-conditions.md diff --git a/Chapter-10-Item-70-Use-checked-exceptions-for-recoverable-conditions-and-runtime-exceptions-for-programming-errors.md b/Chapter-10/Chapter-10-Item-70-Use-checked-exceptions-for-recoverable-conditions-and-runtime-exceptions-for-programming-errors.md similarity index 100% rename from Chapter-10-Item-70-Use-checked-exceptions-for-recoverable-conditions-and-runtime-exceptions-for-programming-errors.md rename to Chapter-10/Chapter-10-Item-70-Use-checked-exceptions-for-recoverable-conditions-and-runtime-exceptions-for-programming-errors.md diff --git a/Chapter-10-Item-71-Avoid-unnecessary-use-of-checked-exceptions.md b/Chapter-10/Chapter-10-Item-71-Avoid-unnecessary-use-of-checked-exceptions.md similarity index 100% rename from Chapter-10-Item-71-Avoid-unnecessary-use-of-checked-exceptions.md rename to Chapter-10/Chapter-10-Item-71-Avoid-unnecessary-use-of-checked-exceptions.md diff --git a/Chapter-10-Item-72-Favor-the-use-of-standard-exceptions.md b/Chapter-10/Chapter-10-Item-72-Favor-the-use-of-standard-exceptions.md similarity index 100% rename from Chapter-10-Item-72-Favor-the-use-of-standard-exceptions.md rename to Chapter-10/Chapter-10-Item-72-Favor-the-use-of-standard-exceptions.md diff --git a/Chapter-10-Item-73-Throw-exceptions-appropriate-to-the-abstraction.md b/Chapter-10/Chapter-10-Item-73-Throw-exceptions-appropriate-to-the-abstraction.md similarity index 100% rename from Chapter-10-Item-73-Throw-exceptions-appropriate-to-the-abstraction.md rename to Chapter-10/Chapter-10-Item-73-Throw-exceptions-appropriate-to-the-abstraction.md diff --git a/Chapter-10-Item-74-Document-all-exceptions-thrown-by-each-method.md b/Chapter-10/Chapter-10-Item-74-Document-all-exceptions-thrown-by-each-method.md similarity index 100% rename from Chapter-10-Item-74-Document-all-exceptions-thrown-by-each-method.md rename to Chapter-10/Chapter-10-Item-74-Document-all-exceptions-thrown-by-each-method.md diff --git a/Chapter-10-Item-75-Include-failure-capture-information-in-detail-messages.md b/Chapter-10/Chapter-10-Item-75-Include-failure-capture-information-in-detail-messages.md similarity index 100% rename from Chapter-10-Item-75-Include-failure-capture-information-in-detail-messages.md rename to Chapter-10/Chapter-10-Item-75-Include-failure-capture-information-in-detail-messages.md diff --git a/Chapter-10-Item-76-Strive-for-failure-atomicity.md b/Chapter-10/Chapter-10-Item-76-Strive-for-failure-atomicity.md similarity index 100% rename from Chapter-10-Item-76-Strive-for-failure-atomicity.md rename to Chapter-10/Chapter-10-Item-76-Strive-for-failure-atomicity.md diff --git "a/Chapter-10-Item-77-Don\342\200\231t-ignore-exceptions.md" "b/Chapter-10/Chapter-10-Item-77-Don\342\200\231t-ignore-exceptions.md" similarity index 100% rename from "Chapter-10-Item-77-Don\342\200\231t-ignore-exceptions.md" rename to "Chapter-10/Chapter-10-Item-77-Don\342\200\231t-ignore-exceptions.md" diff --git a/Chapter-11-Introduction.md b/Chapter-11/Chapter-11-Introduction.md similarity index 100% rename from Chapter-11-Introduction.md rename to Chapter-11/Chapter-11-Introduction.md diff --git a/Chapter-11-Item-78-Synchronize-access-to-shared-mutable-data.md b/Chapter-11/Chapter-11-Item-78-Synchronize-access-to-shared-mutable-data.md similarity index 100% rename from Chapter-11-Item-78-Synchronize-access-to-shared-mutable-data.md rename to Chapter-11/Chapter-11-Item-78-Synchronize-access-to-shared-mutable-data.md diff --git a/Chapter-11-Item-79-Avoid-excessive-synchronization.md b/Chapter-11/Chapter-11-Item-79-Avoid-excessive-synchronization.md similarity index 100% rename from Chapter-11-Item-79-Avoid-excessive-synchronization.md rename to Chapter-11/Chapter-11-Item-79-Avoid-excessive-synchronization.md diff --git a/Chapter-11-Item-80-Prefer-executors,-tasks,-and-streams-to-threads.md b/Chapter-11/Chapter-11-Item-80-Prefer-executors,-tasks,-and-streams-to-threads.md similarity index 100% rename from Chapter-11-Item-80-Prefer-executors,-tasks,-and-streams-to-threads.md rename to Chapter-11/Chapter-11-Item-80-Prefer-executors,-tasks,-and-streams-to-threads.md diff --git a/Chapter-11-Item-81-Prefer-concurrency-utilities-to-wait-and-notify.md b/Chapter-11/Chapter-11-Item-81-Prefer-concurrency-utilities-to-wait-and-notify.md similarity index 100% rename from Chapter-11-Item-81-Prefer-concurrency-utilities-to-wait-and-notify.md rename to Chapter-11/Chapter-11-Item-81-Prefer-concurrency-utilities-to-wait-and-notify.md diff --git a/Chapter-11-Item-82-Document-thread-safety.md b/Chapter-11/Chapter-11-Item-82-Document-thread-safety.md similarity index 100% rename from Chapter-11-Item-82-Document-thread-safety.md rename to Chapter-11/Chapter-11-Item-82-Document-thread-safety.md diff --git a/Chapter-11-Item-83-Use-lazy-initialization-judiciously.md b/Chapter-11/Chapter-11-Item-83-Use-lazy-initialization-judiciously.md similarity index 100% rename from Chapter-11-Item-83-Use-lazy-initialization-judiciously.md rename to Chapter-11/Chapter-11-Item-83-Use-lazy-initialization-judiciously.md diff --git "a/Chapter-11-Item-84-Don\342\200\231t-depend-on-the-thread-scheduler.md" "b/Chapter-11/Chapter-11-Item-84-Don\342\200\231t-depend-on-the-thread-scheduler.md" similarity index 100% rename from "Chapter-11-Item-84-Don\342\200\231t-depend-on-the-thread-scheduler.md" rename to "Chapter-11/Chapter-11-Item-84-Don\342\200\231t-depend-on-the-thread-scheduler.md" diff --git a/Chapter-12-Introduction.md b/Chapter-12/Chapter-12-Introduction.md similarity index 100% rename from Chapter-12-Introduction.md rename to Chapter-12/Chapter-12-Introduction.md diff --git a/Chapter-12-Item-85-Prefer-alternatives-to-Java-serialization.md b/Chapter-12/Chapter-12-Item-85-Prefer-alternatives-to-Java-serialization.md similarity index 100% rename from Chapter-12-Item-85-Prefer-alternatives-to-Java-serialization.md rename to Chapter-12/Chapter-12-Item-85-Prefer-alternatives-to-Java-serialization.md diff --git a/Chapter-12-Item-86-Implement-Serializable-with-great-caution.md b/Chapter-12/Chapter-12-Item-86-Implement-Serializable-with-great-caution.md similarity index 100% rename from Chapter-12-Item-86-Implement-Serializable-with-great-caution.md rename to Chapter-12/Chapter-12-Item-86-Implement-Serializable-with-great-caution.md diff --git a/Chapter-12-Item-87-Consider-using-a-custom-serialized-form.md b/Chapter-12/Chapter-12-Item-87-Consider-using-a-custom-serialized-form.md similarity index 100% rename from Chapter-12-Item-87-Consider-using-a-custom-serialized-form.md rename to Chapter-12/Chapter-12-Item-87-Consider-using-a-custom-serialized-form.md diff --git a/Chapter-12-Item-88-Write-readObject-methods-defensively.md b/Chapter-12/Chapter-12-Item-88-Write-readObject-methods-defensively.md similarity index 100% rename from Chapter-12-Item-88-Write-readObject-methods-defensively.md rename to Chapter-12/Chapter-12-Item-88-Write-readObject-methods-defensively.md diff --git a/Chapter-12-Item-89-For-instance-control-prefer-enum-types-to-readResolve.md b/Chapter-12/Chapter-12-Item-89-For-instance-control-prefer-enum-types-to-readResolve.md similarity index 100% rename from Chapter-12-Item-89-For-instance-control-prefer-enum-types-to-readResolve.md rename to Chapter-12/Chapter-12-Item-89-For-instance-control-prefer-enum-types-to-readResolve.md diff --git a/Chapter-12-Item-90-Consider-serialization-proxies-instead-of-serialized-instances.md b/Chapter-12/Chapter-12-Item-90-Consider-serialization-proxies-instead-of-serialized-instances.md similarity index 100% rename from Chapter-12-Item-90-Consider-serialization-proxies-instead-of-serialized-instances.md rename to Chapter-12/Chapter-12-Item-90-Consider-serialization-proxies-instead-of-serialized-instances.md diff --git a/Chapter-2-Introduction.md b/Chapter-2/Chapter-2-Introduction.md similarity index 100% rename from Chapter-2-Introduction.md rename to Chapter-2/Chapter-2-Introduction.md diff --git a/Chapter-2-Item-1-Consider-static-factory-methods-instead-of-constructors.md b/Chapter-2/Chapter-2-Item-1-Consider-static-factory-methods-instead-of-constructors.md similarity index 85% rename from Chapter-2-Item-1-Consider-static-factory-methods-instead-of-constructors.md rename to Chapter-2/Chapter-2-Item-1-Consider-static-factory-methods-instead-of-constructors.md index 1a19c41..6ba2434 100644 --- a/Chapter-2-Item-1-Consider-static-factory-methods-instead-of-constructors.md +++ b/Chapter-2/Chapter-2-Item-1-Consider-static-factory-methods-instead-of-constructors.md @@ -34,11 +34,11 @@ Because they have names, static factory methods don’t share the restriction di **A second advantage of static factory methods is that, unlike constructors,they are not required to create a new object each time they’re invoked.** This allows immutable classes (Item 17) to use preconstructed instances, or to cache instances as they’re constructed, and dispense them repeatedly to avoid creating unnecessary duplicate(adj. 复制的;二重的) objects. The Boolean.valueOf(boolean) method illustrates this technique: it never creates an object. This technique is similar to the Flyweight pattern [Gamma95]. It can greatly improve performance if equivalent objects are requested often, especially if they are expensive to create. -**静态工厂方法与构造函数相比的第二个优点,静态工厂方法不需要在每次调用时创建新对象。** 这允许不可变类([Item-17](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-17-Minimize-mutability.md))使用预先构造的实例,或在构造实例时缓存实例,并重复分配它们以避免创建不必要的重复对象。Boolean.valueOf(boolean) 方法说明了这种技术:它从不创建对象。这种技术类似于享元模式[Gamma95]。如果经常请求相同的对象,特别是在创建对象的代价很高时,它可以极大地提高性能。 +**静态工厂方法与构造函数相比的第二个优点,静态工厂方法不需要在每次调用时创建新对象。** 这允许不可变类([Item-17](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-17-Minimize-mutability.md))使用预先构造的实例,或在构造实例时缓存实例,并重复分配它们以避免创建不必要的重复对象。Boolean.valueOf(boolean) 方法说明了这种技术:它从不创建对象。这种技术类似于享元模式[Gamma95]。如果经常请求相同的对象,特别是在创建对象的代价很高时,它可以极大地提高性能。 The ability of static factory methods to return the same object from repeated invocations allows classes to maintain strict control over what instances exist at any time. Classes that do this are said to be instance-controlled. There are several reasons to write instance-controlled classes. Instance control allows a class to guarantee that it is a singleton (Item 3) or noninstantiable (Item 4). Also,it allows an immutable(adj. 不变的;不可变的;不能变的) value class (Item 17) to make the guarantee that no two equal instances exist: a.equals(b) if and only if a == b. This is the basis of the Flyweight pattern [Gamma95]. Enum types (Item 34) provide this guarantee(n.保证,保证书;vt.担保). -静态工厂方法在重复调用中能够返回相同对象,这样的能力允许类严格控制任何时候存在的实例。这样做的类被称为实例受控的类。编写实例受控的类有几个原因。实例控制允许一个类来保证它是一个单例([Item-3](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2-Item-3-Enforce-the-singleton-property-with-a-private-constructor-or-an-enum-type.md))或不可实例化的([Item-4](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2-Item-4-Enforce-noninstantiability-with-a-private-constructor.md))。同时,它允许一个不可变的值类([Item-17](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-17-Minimize-mutability.md))保证不存在两个相同的实例:a.equals(b) 当且仅当 a==b。这是享元模式的基础[Gamma95]。枚举类型([Item-34](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-6-Item-34-Use-enums-instead-of-int-constants.md))提供了这种保证。 +静态工厂方法在重复调用中能够返回相同对象,这样的能力允许类严格控制任何时候存在的实例。这样做的类被称为实例受控的类。编写实例受控的类有几个原因。实例控制允许一个类来保证它是一个单例([Item-3](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2/Chapter-2-Item-3-Enforce-the-singleton-property-with-a-private-constructor-or-an-enum-type.md))或不可实例化的([Item-4](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2/Chapter-2-Item-4-Enforce-noninstantiability-with-a-private-constructor.md))。同时,它允许一个不可变的值类([Item-17](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-17-Minimize-mutability.md))保证不存在两个相同的实例:a.equals(b) 当且仅当 a==b。这是享元模式的基础[Gamma95]。枚举类型([Item-34](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-6/Chapter-6-Item-34-Use-enums-instead-of-int-constants.md))提供了这种保证。 **译注:原文 noninstantiable 应修改为 non-instantiable ,译为「不可实例化的」** @@ -48,17 +48,17 @@ The ability of static factory methods to return the same object from repeated in One application of this flexibility(n.灵活性,弹性,适应性) is that an API can return objects without making their classes public. Hiding implementation classes in this fashion leads to a very compact API. This technique lends itself to interface-based frameworks (Item 20), where interfaces provide natural return types for static factory methods. -这种灵活性的一个应用是 API 可以在不公开其类的情况下返回对象。以这种方式隐藏实现类会导致一个非常紧凑的 API。这种技术适用于基于接口的框架([Item-20](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-20-Prefer-interfaces-to-abstract-classes.md)),其中接口为静态工厂方法提供了自然的返回类型。 +这种灵活性的一个应用是 API 可以在不公开其类的情况下返回对象。以这种方式隐藏实现类会导致一个非常紧凑的 API。这种技术适用于基于接口的框架([Item-20](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-20-Prefer-interfaces-to-abstract-classes.md)),其中接口为静态工厂方法提供了自然的返回类型。 Prior to Java 8, interfaces couldn’t have static methods. By convention, static factory methods for an interface named Type were put in a noninstantiable companion(n.同伴,指南;vt.陪伴) class (Item 4) named Types. For example, the Java Collections Framework has forty-five utility implementations of its interfaces, providing unmodifiable collections, synchronized collections, and the like. Nearly all of these implementations are exported via static factory methods in one noninstantiable class (java.util.Collections). The classes of the returned objects are all nonpublic. -在 Java 8 之前,接口不能有静态方法。按照惯例,一个名为 Type 的接口的静态工厂方法被放在一个名为 Types 的不可实例化的伴随类([Item-4](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2-Item-4-Enforce-noninstantiability-with-a-private-constructor.md))中。例如,Java 的 Collections 框架有 45 个接口实用工具实现,提供了不可修改的集合、同步集合等。几乎所有这些实现都是通过一个非实例化类(java.util.Collections)中的静态工厂方法导出的。返回对象的类都是非公共的。 +在 Java 8 之前,接口不能有静态方法。按照惯例,一个名为 Type 的接口的静态工厂方法被放在一个名为 Types 的不可实例化的伴随类([Item-4](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2/Chapter-2-Item-4-Enforce-noninstantiability-with-a-private-constructor.md))中。例如,Java 的 Collections 框架有 45 个接口实用工具实现,提供了不可修改的集合、同步集合等。几乎所有这些实现都是通过一个非实例化类(java.util.Collections)中的静态工厂方法导出的。返回对象的类都是非公共的。 **译注:原文 noninstantiable 应修改为 non-instantiable ,译为「不可实例化的」** The Collections Framework API is much smaller than it would have been had it exported forty-five separate public classes, one for each convenience implementation. It is not just the bulk of the API that is reduced but the conceptual(abj.概念上的) weight: the number and difficulty of the concepts that programmers must master in order to use the API. The programmer knows that the returned object has precisely the API specified by its interface, so there is no need to read additional class documentation for the implementation class. Furthermore, using such a static factory method requires the client to refer to the returned object by interface rather than implementation class, which is generally good practice (Item 64). -Collections 框架 API 比它导出 45 个独立的公共类要小得多,每个公共类对应一个方便的实现。减少的不仅仅是 API 的数量,还有概念上的减少:程序员为了使用 API 必须掌握的概念的数量和难度。程序员知道返回的对象由相关的接口精确地指定的,因此不需要为实现类阅读额外的类文档。此外,使用这种静态工厂方法需要客户端通过接口而不是实现类引用返回的对象,这通常是很好的实际用法([Item-64](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9-Item-64-Refer-to-objects-by-their-interfaces.md))。 +Collections 框架 API 比它导出 45 个独立的公共类要小得多,每个公共类对应一个方便的实现。减少的不仅仅是 API 的数量,还有概念上的减少:程序员为了使用 API 必须掌握的概念的数量和难度。程序员知道返回的对象由相关的接口精确地指定的,因此不需要为实现类阅读额外的类文档。此外,使用这种静态工厂方法需要客户端通过接口而不是实现类引用返回的对象,这通常是很好的实际用法([Item-64](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9/Chapter-9-Item-64-Refer-to-objects-by-their-interfaces.md))。 As of(自..起) Java 8, the restriction that interfaces cannot contain static methods was eliminated, so there is typically little reason to provide a noninstantiable companion class for an interface. Many public static members that would have been at home in such a class should instead be put in the interface itself. Note,however, that it may still be necessary to put the bulk of the implementation code behind these static methods in a separate package-private class. This is because Java 8 requires all static members of an interface to be public. Java 9 allows private static methods, but static fields and static member classes are still required to be public. @@ -70,7 +70,7 @@ As of(自..起) Java 8, the restriction that interfaces cannot contain stati The EnumSet class (Item 36) has no public constructors, only static factories.In the OpenJDK implementation, they return an instance of one of two subclasses, depending on the size of the underlying enum type: if it has sixty-four or fewer elements, as most enum types do, the static factories return a RegularEnumSet instance, which is backed by a single long; if the enum type has sixty-five or more elements, the factories return a JumboEnumSet instance, backed by a long array. -EnumSet 类([Item-36](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-6-Item-36-Use-EnumSet-instead-of-bit-fields.md))没有公共构造函数,只有静态工厂。在 OpenJDK 实现中,它们返回两个子类中的一个实例,这取决于底层 enum 类型的大小:如果它有 64 个或更少的元素,就像大多数 enum 类型一样,静态工厂返回一个 long 类型的 RegularEnumSet 实例;如果 enum 类型有 65 个或更多的元素,工厂将返回一个由 long[] 类型的 JumboEnumSet 实例。 +EnumSet 类([Item-36](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-6/Chapter-6-Item-36-Use-EnumSet-instead-of-bit-fields.md))没有公共构造函数,只有静态工厂。在 OpenJDK 实现中,它们返回两个子类中的一个实例,这取决于底层 enum 类型的大小:如果它有 64 个或更少的元素,就像大多数 enum 类型一样,静态工厂返回一个 long 类型的 RegularEnumSet 实例;如果 enum 类型有 65 个或更多的元素,工厂将返回一个由 long[] 类型的 JumboEnumSet 实例。 The existence of these two implementation classes is invisible to clients. If RegularEnumSet ceased to offer performance advantages for small enum types, it could be eliminated from a future release with no ill effects. Similarly, a future release could add a third or fourth implementation of EnumSet if it proved beneficial for performance. Clients neither know nor care about the class of the object they get back from the factory; they care only that it is some subclass of EnumSet. @@ -86,15 +86,15 @@ There are three essential(n.本质,要素;adj.基本的,精华的) com An optional fourth component of a service provider framework is a service provider interface, which describes a factory object that produce instances of the service interface. In the absence of a service provider interface, implementations must be instantiated reflectively (Item 65). In the case of JDBC, Connection plays the part of the service interface, DriverManager.registerDriver is the provider registration API, DriverManager.getConnection is the service access API, and Driver is the service provider interface. -服务提供者框架的第四个可选组件是服务提供者接口,它描述了产生服务接口实例的工厂对象。在没有服务提供者接口的情况下,必须以反射的方式实例化实现([Item-65](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9-Item-65-Prefer-interfaces-to-reflection.md))。在 JDBC 中,连接扮演服务接口 DriverManager 的角色。DriverManager.registerDriver 是提供商注册的 API,DriverManager.getConnection 是服务访问 API,驱动程序是服务提供者接口。 +服务提供者框架的第四个可选组件是服务提供者接口,它描述了产生服务接口实例的工厂对象。在没有服务提供者接口的情况下,必须以反射的方式实例化实现([Item-65](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9/Chapter-9-Item-65-Prefer-interfaces-to-reflection.md))。在 JDBC 中,连接扮演服务接口 DriverManager 的角色。DriverManager.registerDriver 是提供商注册的 API,DriverManager.getConnection 是服务访问 API,驱动程序是服务提供者接口。 There are many variants of the service provider framework pattern. For example, the service access API can return a richer service interface to clients than the one furnished by providers. This is the Bridge pattern [Gamma95]. Dependency injection frameworks (Item 5) can be viewed as powerful service providers. Since Java 6, the platform includes a general-purpose service provider framework, java.util.ServiceLoader, so you needn’t, and generally shouldn’t, write your own (Item 59). JDBC doesn’t use ServiceLoader, as the former predates(vt.先于) the latter. -服务提供者框架模式有许多变体。例如,服务访问 API 可以向客户端返回比提供者提供的更丰富的服务接口。这是桥接模式[Gamma95]。依赖注入框架([Item-5](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2-Item-5-Prefer-dependency-injection-to-hardwiring-resources.md))可以看作是强大的服务提供者。由于是 Java 6,该平台包括一个通用服务提供者框架 Java.util.ServiceLoader,所以你不需要,通常也不应该写你自己的([Item-59](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9-Item-59-Know-and-use-the-libraries.md))。JDBC 不使用 ServiceLoader,因为前者比后者要早。 +服务提供者框架模式有许多变体。例如,服务访问 API 可以向客户端返回比提供者提供的更丰富的服务接口。这是桥接模式[Gamma95]。依赖注入框架([Item-5](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2/Chapter-2-Item-5-Prefer-dependency-injection-to-hardwiring-resources.md))可以看作是强大的服务提供者。由于是 Java 6,该平台包括一个通用服务提供者框架 Java.util.ServiceLoader,所以你不需要,通常也不应该写你自己的([Item-59](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9/Chapter-9-Item-59-Know-and-use-the-libraries.md))。JDBC 不使用 ServiceLoader,因为前者比后者要早。 **The main limitation of providing only static factory methods is that classes without public or protected constructors cannot be subclassed.** For example, it is impossible to subclass any of the convenience implementation classes in the Collections Framework. Arguably this can be a blessing in disguise because it encourages programmers to use composition instead of inheritance (Item 18), and is required for immutable types (Item 17). -**仅提供静态工厂方法的主要局限是,没有公共或受保护构造函数的类不能被子类化。** 例如,不可能在集合框架中子类化任何方便的实现类。这可能是一种因祸得福的做法,因为它鼓励程序员使用组合而不是继承([Item-18](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-18-Favor-composition-over-inheritance.md)),并且对于不可变的类型([Item-17](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-17-Minimize-mutability.md))是必需的。 +**仅提供静态工厂方法的主要局限是,没有公共或受保护构造函数的类不能被子类化。** 例如,不可能在集合框架中子类化任何方便的实现类。这可能是一种因祸得福的做法,因为它鼓励程序员使用组合而不是继承([Item-18](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-18-Favor-composition-over-inheritance.md)),并且对于不可变的类型([Item-17](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-17-Minimize-mutability.md))是必需的。 **A second shortcoming of static factory methods is that they are hard for programmers to find.** They do not stand out in API documentation in the way that constructors do, so it can be difficult to figure out how to instantiate a class that provides static factory methods instead of constructors. The Javadoc tool may someday draw attention to static factory methods. In the meantime, you can reduce this problem by drawing attention to static factories in class or interface documentation and by adhering to common naming conventions. Here are some common names for static factory methods. This list is far from exhaustive: diff --git a/Chapter-2-Item-2-Consider-a-builder-when-faced-with-many-constructor-parameters.md b/Chapter-2/Chapter-2-Item-2-Consider-a-builder-when-faced-with-many-constructor-parameters.md similarity index 93% rename from Chapter-2-Item-2-Consider-a-builder-when-faced-with-many-constructor-parameters.md rename to Chapter-2/Chapter-2-Item-2-Consider-a-builder-when-faced-with-many-constructor-parameters.md index f060a12..4467d2d 100644 --- a/Chapter-2-Item-2-Consider-a-builder-when-faced-with-many-constructor-parameters.md +++ b/Chapter-2/Chapter-2-Item-2-Consider-a-builder-when-faced-with-many-constructor-parameters.md @@ -61,7 +61,7 @@ Typically this constructor invocation will require many parameters that you don In short, **the telescoping constructor pattern works, but it is hard to write client code when there are many parameters, and harder still to read it.** The reader is left wondering what all those values mean and must carefully count parameters to find out. Long sequences of identically typed parameters can cause subtle bugs. If the client accidentally reverses two such parameters, the compiler won’t complain, but the program will misbehave at runtime (Item 51). -简单地说,**可伸缩构造函数模式可以工作,但是当有很多参数时,编写客户端代码是很困难的,而且读起来更困难。** 读者想知道所有这些值是什么意思,必须仔细清点参数。相同类型参数的长序列会导致细微的错误。如果客户端不小心倒转了两个这样的参数,编译器不会报错,但是程序会在运行时出错([Item-51](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-8-Item-51-Design-method-signatures-carefully.md))。 +简单地说,**可伸缩构造函数模式可以工作,但是当有很多参数时,编写客户端代码是很困难的,而且读起来更困难。** 读者想知道所有这些值是什么意思,必须仔细清点参数。相同类型参数的长序列会导致细微的错误。如果客户端不小心倒转了两个这样的参数,编译器不会报错,但是程序会在运行时出错([Item-51](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-8/Chapter-8-Item-51-Design-method-signatures-carefully.md))。 A second alternative(n.二中择一;adj.供选择的) when you’re faced with many optional parameters in a constructor is the JavaBeans pattern, in which you call a parameterless constructor to create the object and then call setter methods to set each required parameter and each optional parameter of interest: @@ -103,7 +103,7 @@ cocaCola.setCarbohydrate(27); Unfortunately, the JavaBeans pattern has serious disadvantages of its own. Because construction is split(vt.分离,分解) across multiple calls, a JavaBean may be in an inconsistent state partway through its construction. The class does not have the option of enforcing consistency merely by checking the validity of the constructor parameters. Attempting to use an object when it’s in an inconsistent(adj. 不一致的) state may cause failures that are far removed from the code containing the bug and hence(adv.因此) difficult to debug. A related disadvantage is that the JavaBeans pattern precludes the possibility of making a class immutable (Item 17) and requires added effort on the part of the programmer to ensure thread safety. -不幸的是,JavaBean 模式本身有严重的缺点。因为构建是在多个调用之间进行的,所以 JavaBean 可能在构建的过程中处于不一致的状态。该类不能仅通过检查构造函数参数的有效性来强制一致性。在不一致的状态下尝试使用对象可能会导致错误的发生,而包含这些错误的代码很难调试。一个相关的缺点是,JavaBean 模式排除了使类不可变的可能性([Item-17](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-17-Minimize-mutability.md)),并且需要程序员额外的努力来确保线程安全。 +不幸的是,JavaBean 模式本身有严重的缺点。因为构建是在多个调用之间进行的,所以 JavaBean 可能在构建的过程中处于不一致的状态。该类不能仅通过检查构造函数参数的有效性来强制一致性。在不一致的状态下尝试使用对象可能会导致错误的发生,而包含这些错误的代码很难调试。一个相关的缺点是,JavaBean 模式排除了使类不可变的可能性([Item-17](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-17-Minimize-mutability.md)),并且需要程序员额外的努力来确保线程安全。 It is possible to reduce these disadvantages by manually(adv.手动地) “freezing” the object when its construction is complete and not allowing it to be used until frozen, but this variant is unwieldy and rarely used in practice. Moreover, it can cause errors at runtime because the compiler cannot ensure that the programmer calls the freeze method on an object before using it. @@ -111,7 +111,7 @@ It is possible to reduce these disadvantages by manually(adv.手动地) “f Luckily, there is a third alternative that combines the safety of the telescoping constructor pattern with the readability of the JavaBeans pattern. It is a form of the Builder pattern [Gamma95]. Instead of making the desired object directly,the client calls a constructor (or static factory) with all of the required parameters and gets a builder object. Then the client calls setter-like methods on the builder object to set each optional parameter of interest. Finally, the client calls a parameterless build method to generate the object, which is typically immutable. The builder is typically a static member class (Item 24) of the class itbuilds. Here’s how it looks in practice: -幸运的是,还有第三种选择,它结合了可伸缩构造函数模式的安全性和 JavaBean 模式的可读性。它是建造者模式的一种形式[Gamma95]。客户端不直接生成所需的对象,而是使用所有必需的参数调用构造函数(或静态工厂),并获得一个 builder 对象。然后,客户端在构建器对象上调用像 setter 这样的方法来设置每个感兴趣的可选参数。最后,客户端调用一个无参数的构建方法来生成对象,这通常是不可变的。构建器通常是它构建的类的静态成员类([Item-24](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-24-Favor-static-member-classes-over-nonstatic.md))。下面是它在实际应用中的样子: +幸运的是,还有第三种选择,它结合了可伸缩构造函数模式的安全性和 JavaBean 模式的可读性。它是建造者模式的一种形式[Gamma95]。客户端不直接生成所需的对象,而是使用所有必需的参数调用构造函数(或静态工厂),并获得一个 builder 对象。然后,客户端在构建器对象上调用像 setter 这样的方法来设置每个感兴趣的可选参数。最后,客户端调用一个无参数的构建方法来生成对象,这通常是不可变的。构建器通常是它构建的类的静态成员类([Item-24](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-24-Favor-static-member-classes-over-nonstatic.md))。下面是它在实际应用中的样子: ``` // Builder Pattern @@ -189,7 +189,7 @@ This client code is easy to write and, more importantly, easy to read. The Build Validity(n.有效性) checks were omitted(v.遗漏,省略;adj.省去的) for brevity(n.简洁). To detect invalid parameters as soon as possible, check parameter validity in the builder’s constructor and methods.Check invariants involving multiple parameters in the constructor invoked by the build method. To ensure these invariants against attack, do the checks on object fields after copying parameters from the builder (Item 50). If a check fails, throw an IllegalArgumentException (Item 72) whose detail message indicates which parameters are invalid (Item 75). -为了简洁,省略了有效性检查。为了尽快检测无效的参数,请检查构建器的构造函数和方法中的参数有效性。检查构建方法调用的构造函数中涉及多个参数的不变量。为了确保这些不变量不受攻击,在从构建器复制参数之后检查对象字段([Item-50](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-8-Item-50-Make-defensive-copies-when-needed.md))。如果检查失败,抛出一个 IllegalArgumentException([Item-72](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-10-Item-72-Favor-the-use-of-standard-exceptions.md)),它的详细消息指示哪些参数无效([Item-75](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-10-Item-75-Include-failure-capture-information-in-detail-messages.md))。 +为了简洁,省略了有效性检查。为了尽快检测无效的参数,请检查构建器的构造函数和方法中的参数有效性。检查构建方法调用的构造函数中涉及多个参数的不变量。为了确保这些不变量不受攻击,在从构建器复制参数之后检查对象字段([Item-50](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-8/Chapter-8-Item-50-Make-defensive-copies-when-needed.md))。如果检查失败,抛出一个 IllegalArgumentException([Item-72](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-10/Chapter-10-Item-72-Favor-the-use-of-standard-exceptions.md)),它的详细消息指示哪些参数无效([Item-75](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-10/Chapter-10-Item-75-Include-failure-capture-information-in-detail-messages.md))。 The Builder pattern is well suited to class hierarchies. Use a parallel hierarchy of builders, each nested in the corresponding class. Abstract classes have abstract builders; concrete classes have concrete builders. For example,consider an abstract class at the root of a hierarchy representing various kinds of pizza: @@ -228,7 +228,7 @@ public abstract class Pizza { Note that Pizza.Builder is a generic type with a recursive type parameter (Item 30). This, along with the abstract self method, allows method chaining to work properly in subclasses, without the need for casts. This workaround for the fact that Java lacks a self type is known as the simulated self-type idiom. Here are two concrete subclasses of Pizza, one of which represents a standard New-York-style pizza, the other a calzone. The former has a required size parameter,while the latter lets you specify whether sauce should be inside or out: -请注意,Pizza.Builder 是具有递归类型参数的泛型类型([Item-31](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-31-Use-bounded-wildcards-to-increase-API-flexibility.md))。这与抽象 self 方法一起,允许方法链接在子类中正常工作,而不需要强制转换。对于 Java 缺少自类型这一事实,这种变通方法称为模拟自类型习惯用法。这里有两个具体的比萨子类,一个是标准的纽约风格的比萨,另一个是 calzone。前者有一个所需的大小参数,而后者让你指定酱料应该是内部还是外部: +请注意,Pizza.Builder 是具有递归类型参数的泛型类型([Item-31](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-31-Use-bounded-wildcards-to-increase-API-flexibility.md))。这与抽象 self 方法一起,允许方法链接在子类中正常工作,而不需要强制转换。对于 Java 缺少自类型这一事实,这种变通方法称为模拟自类型习惯用法。这里有两个具体的比萨子类,一个是标准的纽约风格的比萨,另一个是 calzone。前者有一个所需的大小参数,而后者让你指定酱料应该是内部还是外部: ``` import java.util.Objects; diff --git a/Chapter-2-Item-3-Enforce-the-singleton-property-with-a-private-constructor-or-an-enum-type.md b/Chapter-2/Chapter-2-Item-3-Enforce-the-singleton-property-with-a-private-constructor-or-an-enum-type.md similarity index 84% rename from Chapter-2-Item-3-Enforce-the-singleton-property-with-a-private-constructor-or-an-enum-type.md rename to Chapter-2/Chapter-2-Item-3-Enforce-the-singleton-property-with-a-private-constructor-or-an-enum-type.md index 8f19e4f..1b5a8ba 100644 --- a/Chapter-2-Item-3-Enforce-the-singleton-property-with-a-private-constructor-or-an-enum-type.md +++ b/Chapter-2/Chapter-2-Item-3-Enforce-the-singleton-property-with-a-private-constructor-or-an-enum-type.md @@ -4,7 +4,7 @@ A singleton is simply a class that is instantiated(v.实例化) exactly once [Gamma95].Singletons typically represent either a stateless object such as a function (Item24) or a system component that is intrinsically unique. **Making a class a singleton can make it difficult to test its clients** because it’s impossible to substitute a mock implementation for a singleton unless it implements an interface that serves as its type. -单例是一个只实例化一次的类 [Gamma95]。单例通常表示无状态对象,比如函数([Item-24](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-24-Favor-static-member-classes-over-nonstatic.md))或系统组件,它们在本质上是唯一的。**将一个类设计为单例会使它的客户端测试时变得困难,** 除非它实现了作为其类型的接口,否则无法用模拟实现来代替单例。 +单例是一个只实例化一次的类 [Gamma95]。单例通常表示无状态对象,比如函数([Item-24](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-24-Favor-static-member-classes-over-nonstatic.md))或系统组件,它们在本质上是唯一的。**将一个类设计为单例会使它的客户端测试时变得困难,** 除非它实现了作为其类型的接口,否则无法用模拟实现来代替单例。 There are two common ways to implement singletons. Both are based on keeping the constructor private and exporting a public static member to provide access to the sole instance. In one approach, the member is a final field: @@ -21,7 +21,7 @@ public class Elvis { The private constructor is called only once, to initialize the public static final field Elvis.INSTANCE. The lack of a public or protected constructor guarantees a “monoelvistic” universe: exactly one Elvis instance will exist once the Elvis class is initialized—no more, no less. Nothing that a client does can change this, with one caveat: a privileged client can invoke the private constructor reflectively (Item 65) with the aid of the AccessibleObject.setAccessible method. If you need to defend against this attack, modify the constructor to make it throw an exception if it’s asked to create a second instance. -私有构造函数只调用一次,用于初始化 public static final 修饰的 Elvis 类型字段 INSTANCE。不使用 public 或 protected 的构造函数保证了「独一无二」的空间:一旦初始化了 Elvis 类,就只会存在一个 Elvis 实例,不多也不少。客户端所做的任何事情都不能改变这一点,但有一点需要注意:拥有特殊权限的客户端可以借助 AccessibleObject.setAccessible 方法利用反射调用私有构造函数([Item-65](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9-Item-65-Prefer-interfaces-to-reflection.md))如果需要防范这种攻击,请修改构造函数,使其在请求创建第二个实例时抛出异常。 +私有构造函数只调用一次,用于初始化 public static final 修饰的 Elvis 类型字段 INSTANCE。不使用 public 或 protected 的构造函数保证了「独一无二」的空间:一旦初始化了 Elvis 类,就只会存在一个 Elvis 实例,不多也不少。客户端所做的任何事情都不能改变这一点,但有一点需要注意:拥有特殊权限的客户端可以借助 AccessibleObject.setAccessible 方法利用反射调用私有构造函数([Item-65](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9/Chapter-9-Item-65-Prefer-interfaces-to-reflection.md))如果需要防范这种攻击,请修改构造函数,使其在请求创建第二个实例时抛出异常。 ***译注:使用 AccessibleObject.setAccessible 方法调用私有构造函数示例:*** ``` @@ -64,7 +64,7 @@ One advantage of the static factory approach is that it gives you the flexibilit ***译注:static factory approach 等同于 static factory method*** -静态工厂方法的一个优点是,它可以在不更改 API 的情况下决定类是否是单例。工厂方法返回唯一的实例,但是可以对其进行修改,为调用它的每个线程返回一个单独的实例。第二个优点是,如果应用程序需要的话,可以编写泛型的单例工厂([Item-30](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-30-Favor-generic-methods.md))。使用静态工厂的最后一个优点是方法引用能够作为一个提供者,例如 `Elvis::getInstance` 是 `Supplier` 的提供者。除非能够与这些优点沾边,否则使用 public 字段的方式更可取。 +静态工厂方法的一个优点是,它可以在不更改 API 的情况下决定类是否是单例。工厂方法返回唯一的实例,但是可以对其进行修改,为调用它的每个线程返回一个单独的实例。第二个优点是,如果应用程序需要的话,可以编写泛型的单例工厂([Item-30](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-30-Favor-generic-methods.md))。使用静态工厂的最后一个优点是方法引用能够作为一个提供者,例如 `Elvis::getInstance` 是 `Supplier` 的提供者。除非能够与这些优点沾边,否则使用 public 字段的方式更可取。 ***译注 1:原文方法引用可能是笔误,修改为 `Elvis::getInstance`*** @@ -77,7 +77,7 @@ obj.leaveTheBuilding(); To make a singleton class that uses either of these approaches serializable (Chapter 12), it is not sufficient merely to add implements Serializable to its declaration. To maintain(vt.维持) the singleton guarantee(n.保证), declare all instance fields transient and provide a readResolve method (Item 89). Otherwise, each time a serialized instance is deserialized, a new instance will be created, leading,in the case of our example, to spurious(adj.虚假的) Elvis sightings. To prevent this from happening, add this readResolve method to the Elvis class: -要使单例类使用这两种方法中的任何一种([12-Serialization]( https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-12-Introduction.md)),仅仅在其声明中添加实现 serializable 是不够的。要维护单例保证,应声明所有实例字段为 transient,并提供 readResolve 方法([Item-89](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-12-Item-89-For-instance-control-prefer-enum-types-to-readResolve.md))。否则,每次反序列化实例时,都会创建一个新实例,在我们的示例中,这会导致出现虚假的 Elvis。为了防止这种情况发生,将这个 readResolve 方法添加到 Elvis 类中: +要使单例类使用这两种方法中的任何一种([12-Serialization]( https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-12/Chapter-12-Introduction.md)),仅仅在其声明中添加实现 serializable 是不够的。要维护单例保证,应声明所有实例字段为 transient,并提供 readResolve 方法([Item-89](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-12/Chapter-12-Item-89-For-instance-control-prefer-enum-types-to-readResolve.md))。否则,每次反序列化实例时,都会创建一个新实例,在我们的示例中,这会导致出现虚假的 Elvis。为了防止这种情况发生,将这个 readResolve 方法添加到 Elvis 类中: ``` // readResolve method to preserve singleton property diff --git a/Chapter-2-Item-4-Enforce-noninstantiability-with-a-private-constructor.md b/Chapter-2/Chapter-2-Item-4-Enforce-noninstantiability-with-a-private-constructor.md similarity index 87% rename from Chapter-2-Item-4-Enforce-noninstantiability-with-a-private-constructor.md rename to Chapter-2/Chapter-2-Item-4-Enforce-noninstantiability-with-a-private-constructor.md index 08467ff..3a1aa56 100644 --- a/Chapter-2-Item-4-Enforce-noninstantiability-with-a-private-constructor.md +++ b/Chapter-2/Chapter-2-Item-4-Enforce-noninstantiability-with-a-private-constructor.md @@ -4,7 +4,7 @@ Occasionally(adv.偶尔) you’ll want to write a class that is just a grouping of static methods and static fields. Such classes have acquired(v.取得) a bad reputation(n.名声) because some people abuse them to avoid thinking in terms of objects, but they do have valid uses. They can be used to group related methods on primitive values or arrays, in the manner of java.lang.Math or java.util.Arrays. They can also be used to group static methods, including factories (Item 1), for objects that implement some interface, in the manner of java.util.Collections. (As of Java 8, you can also put such methods in the interface, assuming it’s yours to modify.) Lastly, such classes can be used to group methods on a final class, since you can’t put them in a subclass. -有时你会想要写一个类,它只是一个静态方法和静态字段的组合。这样的类已经获得了坏名声,因为有些人滥用它们来避免从对象角度思考,但是它们确有用途。它们可以用 java.lang.Math 或 java.util.Arrays 的方式,用于与原始值或数组相关的方法。它们还可以用于对以 java.util.Collections 的方式实现某些接口的对象分组静态方法,包括工厂([Item-1](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2-Item-1-Consider-static-factory-methods-instead-of-constructors.md))。(对于 Java 8,你也可以将这些方法放入接口中,假设你可以进行修改。)最后,这些类可用于对 final 类上的方法进行分组,因为你不能将它们放在子类中。 +有时你会想要写一个类,它只是一个静态方法和静态字段的组合。这样的类已经获得了坏名声,因为有些人滥用它们来避免从对象角度思考,但是它们确有用途。它们可以用 java.lang.Math 或 java.util.Arrays 的方式,用于与原始值或数组相关的方法。它们还可以用于对以 java.util.Collections 的方式实现某些接口的对象分组静态方法,包括工厂([Item-1](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2/Chapter-2-Item-1-Consider-static-factory-methods-instead-of-constructors.md))。(对于 Java 8,你也可以将这些方法放入接口中,假设你可以进行修改。)最后,这些类可用于对 final 类上的方法进行分组,因为你不能将它们放在子类中。 Such utility classes were not designed to be instantiated: an instance would be nonsensical. In the absence of explicit constructors, however, the compiler provides a public, parameterless default constructor. To a user, this constructor is indistinguishable from any other. It is not uncommon to see unintentionally instantiable classes in published APIs. @@ -14,7 +14,7 @@ Such utility classes were not designed to be instantiated: an instance would be **译注:原文 noninstantiable 应修改为 non-instantiable ,译为「不可实例化的」** -**试图通过使类抽象来实施不可实例化是行不通的。** 可以对类进行子类化,并实例化子类。此外,它误导用户认为类是为继承而设计的([Item-19](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-19-Design-and-document-for-inheritance-or-else-prohibit-it.md))。然而,有一个简单的习惯用法来确保不可实例化。只有当类不包含显式构造函数时,才会生成默认构造函数,因此**可以通过包含私有构造函数使类不可实例化:** +**试图通过使类抽象来实施不可实例化是行不通的。** 可以对类进行子类化,并实例化子类。此外,它误导用户认为类是为继承而设计的([Item-19](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-19-Design-and-document-for-inheritance-or-else-prohibit-it.md))。然而,有一个简单的习惯用法来确保不可实例化。只有当类不包含显式构造函数时,才会生成默认构造函数,因此**可以通过包含私有构造函数使类不可实例化:** ``` // Noninstantiable utility class diff --git a/Chapter-2-Item-5-Prefer-dependency-injection-to-hardwiring-resources.md b/Chapter-2/Chapter-2-Item-5-Prefer-dependency-injection-to-hardwiring-resources.md similarity index 87% rename from Chapter-2-Item-5-Prefer-dependency-injection-to-hardwiring-resources.md rename to Chapter-2/Chapter-2-Item-5-Prefer-dependency-injection-to-hardwiring-resources.md index 0fee266..b49956b 100644 --- a/Chapter-2-Item-5-Prefer-dependency-injection-to-hardwiring-resources.md +++ b/Chapter-2/Chapter-2-Item-5-Prefer-dependency-injection-to-hardwiring-resources.md @@ -4,7 +4,7 @@ Many classes depend on one or more underlying(adj.潜在的,根本的) resources. For example, a spell checker depends on a dictionary. It is not uncommon to see such classes implemented as static utility classes (Item 4): -许多类依赖于一个或多个底层资源。例如,拼写检查程序依赖于字典。常见做法是,将这种类实现为静态实用工具类([Item-4](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2-Item-4-Enforce-noninstantiability-with-a-private-constructor.md)): +许多类依赖于一个或多个底层资源。例如,拼写检查程序依赖于字典。常见做法是,将这种类实现为静态实用工具类([Item-4](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2/Chapter-2-Item-4-Enforce-noninstantiability-with-a-private-constructor.md)): ``` // Inappropriate use of static utility - inflexible & untestable! @@ -18,7 +18,7 @@ public class SpellChecker { Similarly, it’s not uncommon to see them implemented as singletons (Item 3): -类似地,我们也经常看到它们的单例实现([Item-3](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2-Item-3-Enforce-the-singleton-property-with-a-private-constructor-or-an-enum-type.md)): +类似地,我们也经常看到它们的单例实现([Item-3](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2/Chapter-2-Item-3-Enforce-the-singleton-property-with-a-private-constructor-or-an-enum-type.md)): ``` // Inappropriate use of singleton - inflexible & untestable! @@ -57,11 +57,11 @@ public class SpellChecker { The dependency injection pattern is so simple that many programmers use it for years without knowing it has a name. While our spell checker example had only a single resource (the dictionary), dependency injection works with an arbitrary(adj.任意的) number of resources and arbitrary dependency graphs. It preserves immutability (Item 17), so multiple clients can share dependent objects(assuming the clients desire the same underlying resources). Dependency injection is equally applicable to constructors, static factories (Item 1), and builders (Item 2). -依赖注入模式非常简单,许多程序员在不知道其名称的情况下使用了多年。虽然拼写检查器示例只有一个资源(字典),但是依赖注入可以处理任意数量的资源和任意依赖路径。它保持了不可变性([Item-17](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-17-Minimize-mutability.md)),因此多个客户端可以共享依赖对象(假设客户端需要相同的底层资源)。依赖注入同样适用于构造函数、静态工厂([Item-1](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2-Item-1-Consider-static-factory-methods-instead-of-constructors.md))和构建器([Item-2](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2-Item-2-Consider-a-builder-when-faced-with-many-constructor-parameters.md))。 +依赖注入模式非常简单,许多程序员在不知道其名称的情况下使用了多年。虽然拼写检查器示例只有一个资源(字典),但是依赖注入可以处理任意数量的资源和任意依赖路径。它保持了不可变性([Item-17](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-17-Minimize-mutability.md)),因此多个客户端可以共享依赖对象(假设客户端需要相同的底层资源)。依赖注入同样适用于构造函数、静态工厂([Item-1](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2/Chapter-2-Item-1-Consider-static-factory-methods-instead-of-constructors.md))和构建器([Item-2](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2/Chapter-2-Item-2-Consider-a-builder-when-faced-with-many-constructor-parameters.md))。 A useful variant of the pattern is to pass a resource factory to the constructor.A factory is an object that can be called repeatedly to create instances of a type.Such factories embody the Factory Method pattern [Gamma95]. The `Supplier` interface, introduced in Java 8, is perfect for representing factories. Methods that take a Supplier on input should typically constrain the factory’s type parameter using a bounded wildcard type (Item 31) to allow the client to pass in a factory that creates any subtype of a specified type. For example, here is a method that makes a mosaic using a client-provided factory to produce each tile: -这种模式的一个有用变体是将资源工厂传递给构造函数。工厂是一个对象,可以反复调用它来创建类型的实例。这样的工厂体现了工厂方法模式 [Gamma95]。Java 8 中引入的 `Supplier` 非常适合表示工厂。在输入中接受 `Supplier` 的方法通常应该使用有界通配符类型([Item-31](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-31-Use-bounded-wildcards-to-increase-API-flexibility.md))来约束工厂的类型参数,以允许客户端传入创建指定类型的任何子类型的工厂。例如,这里有一个生产瓷砖方法,每块瓷砖都使用客户提供的工厂来制作马赛克: +这种模式的一个有用变体是将资源工厂传递给构造函数。工厂是一个对象,可以反复调用它来创建类型的实例。这样的工厂体现了工厂方法模式 [Gamma95]。Java 8 中引入的 `Supplier` 非常适合表示工厂。在输入中接受 `Supplier` 的方法通常应该使用有界通配符类型([Item-31](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-31-Use-bounded-wildcards-to-increase-API-flexibility.md))来约束工厂的类型参数,以允许客户端传入创建指定类型的任何子类型的工厂。例如,这里有一个生产瓷砖方法,每块瓷砖都使用客户提供的工厂来制作马赛克: ``` Mosaic create(Supplier tileFactory) { ... } diff --git a/Chapter-2-Item-6-Avoid-creating-unnecessary-objects.md b/Chapter-2/Chapter-2-Item-6-Avoid-creating-unnecessary-objects.md similarity index 87% rename from Chapter-2-Item-6-Avoid-creating-unnecessary-objects.md rename to Chapter-2/Chapter-2-Item-6-Avoid-creating-unnecessary-objects.md index e0c73f1..98581a7 100644 --- a/Chapter-2-Item-6-Avoid-creating-unnecessary-objects.md +++ b/Chapter-2/Chapter-2-Item-6-Avoid-creating-unnecessary-objects.md @@ -4,7 +4,7 @@ It is often appropriate to reuse a single object instead of creating a new functionally equivalent object each time it is needed. Reuse can be both faster and more stylish. An object can always be reused if it is immutable (Item 17). -重用单个对象通常是合适的,不必每次需要时都创建一个新的功能等效对象。重用可以更快、更时尚。如果对象是不可变的,那么它总是可以被重用的([Item-17](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-17-Minimize-mutability.md))。 +重用单个对象通常是合适的,不必每次需要时都创建一个新的功能等效对象。重用可以更快、更时尚。如果对象是不可变的,那么它总是可以被重用的([Item-17](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-17-Minimize-mutability.md))。 As an extreme example of what not to do, consider this statement: @@ -32,7 +32,7 @@ This version uses a single String instance, rather than creating a new one each You can often avoid creating unnecessary objects by using static factory methods (Item 1) in preference to constructors on immutable classes that provide both. For example, the factory method Boolean.valueOf(String) is preferable to the constructor Boolean(String), which was deprecated in Java 9. The constructor must create a new object each time it’s called, while the factory method is never required to do so and won’t in practice. In addition to reusing immutable objects, you can also reuse mutable objects if you know they won’t be modified. -你通常可以通过使用静态工厂方法([Item-1](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2-Item-1-Consider-static-factory-methods-instead-of-constructors.md))来避免创建不必要的对象,而不是在提供这两种方法的不可变类上使用构造函数。例如,工厂方法 Boolean.valueOf(String) 比构造函数 ~~Boolean(String)~~ 更可取,**后者在 Java 9 中被弃用了** 。构造函数每次调用时都必须创建一个新对象,而工厂方法从来不需要这样做,在实际应用中也不会这样做。除了重用不可变对象之外,如果知道可变对象不会被修改,也可以重用它们。 +你通常可以通过使用静态工厂方法([Item-1](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2/Chapter-2-Item-1-Consider-static-factory-methods-instead-of-constructors.md))来避免创建不必要的对象,而不是在提供这两种方法的不可变类上使用构造函数。例如,工厂方法 Boolean.valueOf(String) 比构造函数 ~~Boolean(String)~~ 更可取,**后者在 Java 9 中被弃用了** 。构造函数每次调用时都必须创建一个新对象,而工厂方法从来不需要这样做,在实际应用中也不会这样做。除了重用不可变对象之外,如果知道可变对象不会被修改,也可以重用它们。 Some object creations are much more expensive than others. If you’re going to need such an “expensive object” repeatedly, it may be advisable(adj.明智的,适当的) to cache it for reuse. Unfortunately, it’s not always obvious when you’re creating such an object. Suppose you want to write a method to determine(v.下决心;vt.确定) whether a string is a valid Roman numeral. Here’s the easiest way to do this using a regular expression: @@ -69,7 +69,7 @@ The improved version of isRomanNumeral provides significant performance gains if If the class containing the improved version of the isRomanNumeral method is initialized but the method is never invoked, the field ROMAN will be initialized needlessly. It would be possible to eliminate the initialization by lazily initializing the field (Item 83) the first time the isRomanNumeral method is invoked, but this is not recommended. As is often the case with lazy initialization, it would complicate the implementation with no measurable performance improvement (Item 67). -如果加载包含改进版 isRomanNumeral 方法的类时,该方法从未被调用过,那么初始化字段 ROMAN 是不必要的。因此,可以用延迟初始化字段([Item-83](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-11-Item-83-Use-lazy-initialization-judiciously.md))的方式在第一次调用 isRomanNumeral 方法时才初始化字段,而不是在类加载时初始化,但不建议这样做。通常情况下,延迟初始化会使实现复杂化,而没有明显的性能改善([Item-67](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9-Item-67-Optimize-judiciously.md))。 +如果加载包含改进版 isRomanNumeral 方法的类时,该方法从未被调用过,那么初始化字段 ROMAN 是不必要的。因此,可以用延迟初始化字段([Item-83](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-11/Chapter-11-Item-83-Use-lazy-initialization-judiciously.md))的方式在第一次调用 isRomanNumeral 方法时才初始化字段,而不是在类加载时初始化,但不建议这样做。通常情况下,延迟初始化会使实现复杂化,而没有明显的性能改善([Item-67](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9/Chapter-9-Item-67-Optimize-judiciously.md))。 ***译注:类加载通常指的是类的生命周期中加载、连接、初始化三个阶段。当方法没有在类加载过程中被使用时,可以不初始化与之相关的字段*** @@ -83,7 +83,7 @@ For example, the keySet method of the Map interface returns a Set view of the Ma Another way to create unnecessary objects is autoboxing, which allows the programmer to mix primitive and boxed primitive types, boxing and unboxing automatically as needed. **Autoboxing blurs but does not erase the distinction between primitive and boxed primitive types.** There are subtle semantic distinctions and not-so-subtle performance differences (Item 61). Consider the following method, which calculates the sum of all the positive int values. To do this, the program has to use long arithmetic because an int is not big enough to hold the sum of all the positive int values: -另一种创建不必要对象的方法是自动装箱,它允许程序员混合原始类型和包装类型,根据需要自动装箱和拆箱。**自动装箱模糊了原始类型和包装类型之间的区别,** 两者有细微的语义差别和不明显的性能差别([Item-61](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9-Item-61-Prefer-primitive-types-to-boxed-primitives.md))。考虑下面的方法,它计算所有正整数的和。为了做到这一点,程序必须使用 long,因为 int 值不够大,不足以容纳所有正整数值的和: +另一种创建不必要对象的方法是自动装箱,它允许程序员混合原始类型和包装类型,根据需要自动装箱和拆箱。**自动装箱模糊了原始类型和包装类型之间的区别,** 两者有细微的语义差别和不明显的性能差别([Item-61](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9/Chapter-9-Item-61-Prefer-primitive-types-to-boxed-primitives.md))。考虑下面的方法,它计算所有正整数的和。为了做到这一点,程序必须使用 long,因为 int 值不够大,不足以容纳所有正整数值的和: ``` // Hideously slow! Can you spot the object creation? @@ -109,4 +109,4 @@ Conversely, avoiding object creation by maintaining your own object pool is a ba The counterpoint to this item is Item 50 on defensive copying. The present item says, “Don’t create a new object when you should reuse an existing one,”while Item 50 says, “Don’t reuse an existing object when you should create a new one.” Note that the penalty for reusing an object when defensive copying is called for is far greater than the penalty for needlessly creating a duplicate object. Failing to make defensive copies where required can lead to insidious bugs and security holes; creating objects unnecessarily merely affects style and performance. -与此项对应的条目是 [Item-50](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-8-Item-50-Make-defensive-copies-when-needed.md)(防御性复制)。当前项的描述是:「在应该重用现有对象时不要创建新对象」,而 Item 50 的描述则是:「在应该创建新对象时不要重用现有对象」。请注意,当需要进行防御性复制时,重用对象所受到的惩罚远远大于不必要地创建重复对象所受到的惩罚。在需要时不制作防御性副本可能导致潜在的 bug 和安全漏洞;不必要地创建对象只会影响样式和性能。 +与此项对应的条目是 [Item-50](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-8/Chapter-8-Item-50-Make-defensive-copies-when-needed.md)(防御性复制)。当前项的描述是:「在应该重用现有对象时不要创建新对象」,而 Item 50 的描述则是:「在应该创建新对象时不要重用现有对象」。请注意,当需要进行防御性复制时,重用对象所受到的惩罚远远大于不必要地创建重复对象所受到的惩罚。在需要时不制作防御性副本可能导致潜在的 bug 和安全漏洞;不必要地创建对象只会影响样式和性能。 diff --git a/Chapter-2-Item-7-Eliminate-obsolete-object-references.md b/Chapter-2/Chapter-2-Item-7-Eliminate-obsolete-object-references.md similarity index 94% rename from Chapter-2-Item-7-Eliminate-obsolete-object-references.md rename to Chapter-2/Chapter-2-Item-7-Eliminate-obsolete-object-references.md index b150e18..24f7d19 100644 --- a/Chapter-2-Item-7-Eliminate-obsolete-object-references.md +++ b/Chapter-2/Chapter-2-Item-7-Eliminate-obsolete-object-references.md @@ -48,7 +48,7 @@ public class Stack { There’s nothing obviously wrong with this program (but see Item 29 for a generic version). You could test it exhaustively, and it would pass every test with flying colors, but there’s a problem lurking. Loosely speaking, the program has a”memory leak,” which can silently manifest itself as reduced performance due to increased garbage collector activity or increased memory footprint. In extreme cases, such memory leaks can cause disk paging and even program failure with an OutOfMemoryError, but such failures are relatively rare. -这个程序没有明显的错误(但是通用版本请参阅 [Item-29](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-29-Favor-generic-types.md))。你可以对它进行详尽的测试,它会以优异的成绩通过所有的测试,但是有一个潜在的问题。简单地说,该程序有一个「内存泄漏」问题,由于垃圾收集器活动的增加或内存占用的增加,它可以悄无声息地表现为性能的降低。在极端情况下,这种内存泄漏可能导致磁盘分页,甚至出现 OutOfMemoryError 程序故障,但这种故障相对少见。 +这个程序没有明显的错误(但是通用版本请参阅 [Item-29](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-29-Favor-generic-types.md))。你可以对它进行详尽的测试,它会以优异的成绩通过所有的测试,但是有一个潜在的问题。简单地说,该程序有一个「内存泄漏」问题,由于垃圾收集器活动的增加或内存占用的增加,它可以悄无声息地表现为性能的降低。在极端情况下,这种内存泄漏可能导致磁盘分页,甚至出现 OutOfMemoryError 程序故障,但这种故障相对少见。 So where is the memory leak? If a stack grows and then shrinks, the objects that were popped off the stack will not be garbage collected, even if the program using the stack has no more references to them. This is because the stack maintains obsolete references to these objects. An obsolete reference is simply a reference that will never be dereferenced again. In this case, any references outside of the “active portion” of the element array are obsolete. The active portion consists of the elements whose index is less than size. @@ -78,7 +78,7 @@ An added benefit of nulling out obsolete references is that if they are subseque When programmers are first stung by this problem, they may overcompensate(vt.给予…过度补偿) by nulling out every object reference as soon as the program is finished using it.This is neither necessary nor desirable; it clutters up the program unnecessarily.Nulling out object references should be the exception rather than the norm.The best way to eliminate an obsolete reference is to let the variable that contained the reference fall out of scope. This occurs naturally if you define each variable in the narrowest possible scope (Item 57). -当程序员第一次被这个问题困扰时,他们可能会过度担心,一旦程序使用完它,他们就会取消所有对象引用。这既无必要也不可取;它不必要地搞乱了程序。清除对象引用应该是例外,而不是规范。消除过时引用的最佳方法是让包含引用的变量脱离作用域。如果你在最狭窄的范围内定义每个变量([Item-57](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9-Item-57-Minimize-the-scope-of-local-variables.md)),那么这种情况自然会发生。 +当程序员第一次被这个问题困扰时,他们可能会过度担心,一旦程序使用完它,他们就会取消所有对象引用。这既无必要也不可取;它不必要地搞乱了程序。清除对象引用应该是例外,而不是规范。消除过时引用的最佳方法是让包含引用的变量脱离作用域。如果你在最狭窄的范围内定义每个变量([Item-57](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9/Chapter-9-Item-57-Minimize-the-scope-of-local-variables.md)),那么这种情况自然会发生。 So when should you null out a reference? What aspect of the Stack class makes it susceptible to memory leaks? Simply put, it manages its own memory.The storage pool consists of the elements of the elements array (the object reference cells, not the objects themselves). The elements in the active portion of the array (as defined earlier) are allocated, and those in the remainder of the array are free. The garbage collector has no way of knowing this; to the garbage collector, all of the object references in the elements array are equally valid.Only the programmer knows that the inactive portion of the array is unimportant.The programmer effectively communicates this fact to the garbage collector by manually nulling out array elements as soon as they become part of the inactive portion. diff --git a/Chapter-2-Item-8-Avoid-finalizers-and-cleaners.md b/Chapter-2/Chapter-2-Item-8-Avoid-finalizers-and-cleaners.md similarity index 94% rename from Chapter-2-Item-8-Avoid-finalizers-and-cleaners.md rename to Chapter-2/Chapter-2-Item-8-Avoid-finalizers-and-cleaners.md index 6b104ce..138ddea 100644 --- a/Chapter-2-Item-8-Avoid-finalizers-and-cleaners.md +++ b/Chapter-2/Chapter-2-Item-8-Avoid-finalizers-and-cleaners.md @@ -8,7 +8,7 @@ C++ programmers are cautioned not to think of finalizers or cleaners as Java’s analogue of C++ destructors. In C++, destructors are the normal way to reclaim the resources associated with an object, a necessary counterpart to constructors.In Java, the garbage collector reclaims the storage associated with an object when it becomes unreachable, requiring no special effort on the part of the programmer. C++ destructors are also used to reclaim other nonmemory resources. In Java, a try-with-resources or try-finally block is used for this purpose (Item 9). -c++ 程序员被告诫不要把终结器或清除器当成 Java 的 c++ 析构函数。在 c++ 中,析构函数是回收与对象相关联的资源的常用方法,对象是构造函数的必要对等物。在 Java 中,当对象变得不可访问时,垃圾收集器将回收与之关联的存储,无需程序员进行任何特殊工作。c++ 析构函数还用于回收其他非内存资源。在 Java 中,使用带有资源的 try-with-resources 或 try-finally 块用于此目的([Item-9](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2-Item-9-Prefer-try-with-resources-to-try-finally.md))。 +c++ 程序员被告诫不要把终结器或清除器当成 Java 的 c++ 析构函数。在 c++ 中,析构函数是回收与对象相关联的资源的常用方法,对象是构造函数的必要对等物。在 Java 中,当对象变得不可访问时,垃圾收集器将回收与之关联的存储,无需程序员进行任何特殊工作。c++ 析构函数还用于回收其他非内存资源。在 Java 中,使用带有资源的 try-with-resources 或 try-finally 块用于此目的([Item-9](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2/Chapter-2-Item-9-Prefer-try-with-resources-to-try-finally.md))。 One shortcoming of finalizers and cleaners is that there is no guarantee they’ll be executed promptly [JLS, 12.6]. It can take arbitrarily long between the time that an object becomes unreachable and the time its finalizer or cleaner runs.This means that you should never do anything time-critical in a finalizer or cleaner. For example, it is a grave error to depend on a finalizer or cleaner to close files because open file descriptors are a limited resource. If many files are left open as a result of the system’s tardiness in running finalizers or cleaners, a program may fail because it can no longer open files. @@ -40,11 +40,11 @@ There is a severe performance penalty for using finalizers and cleaners.On my ma Finalizers have a serious security problem: they open your class up to finalizer attacks. The idea behind a finalizer attack is simple: If an exception is thrown from a constructor or its serialization equivalents—the readObject and readResolve methods (Chapter 12)—the finalizer of a malicious subclass can run on the partially constructed object that should have “died on the vine.” This finalizer can record a reference to the object in a static field,preventing it from being garbage collected. Once the malformed object has been recorded, it is a simple matter to invoke arbitrary methods on this object that should never have been allowed to exist in the first place. Throwing an exception from a constructor should be sufficient to prevent an object from coming into existence; in the presence of finalizers, it is not. Such attacks can have dire consequences. Final classes are immune to finalizer attacks because no one can write a malicious subclass of a final class. To protect nonfinal classes from finalizer attacks, write a final finalize method that does nothing. -终结器有一个严重的安全问题:它们会让你的类受到终结器攻击。终结器攻击背后的思想很简单:如果从构造函数或它的序列化等价物(readObject 和 readResolve 方法([Item-12](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3-Item-12-Always-override-toString.md)))抛出一个异常,恶意子类的终结器就可以运行在部分构造的对象上,而这个对象本来应该「胎死腹中」。这个终结器可以在静态字段中记录对对象的引用,防止它被垃圾收集。一旦记录了畸形对象,就很容易在这个对象上调用本来就不应该存在的任意方法。从构造函数抛出异常应该足以防止对象的出现;在有终结器的情况下,就不是这样了。这样的攻击可能会造成可怕的后果。最终类对终结器攻击免疫,因为没有人能够编写最终类的恶意子类。为了保护非最终类不受终结器攻击,编写一个不执行任何操作的最终终结方法。 +终结器有一个严重的安全问题:它们会让你的类受到终结器攻击。终结器攻击背后的思想很简单:如果从构造函数或它的序列化等价物(readObject 和 readResolve 方法([Item-12](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3/Chapter-3-Item-12-Always-override-toString.md)))抛出一个异常,恶意子类的终结器就可以运行在部分构造的对象上,而这个对象本来应该「胎死腹中」。这个终结器可以在静态字段中记录对对象的引用,防止它被垃圾收集。一旦记录了畸形对象,就很容易在这个对象上调用本来就不应该存在的任意方法。从构造函数抛出异常应该足以防止对象的出现;在有终结器的情况下,就不是这样了。这样的攻击可能会造成可怕的后果。最终类对终结器攻击免疫,因为没有人能够编写最终类的恶意子类。为了保护非最终类不受终结器攻击,编写一个不执行任何操作的最终终结方法。 So what should you do instead of writing a finalizer or cleaner for a class whose objects encapsulate resources that require termination, such as files or threads? Just have your class implement AutoCloseable, and require its clients to invoke the close method on each instance when it is no longer needed, typically using try-with-resources to ensure termination even in the face of exceptions (Item 9). One detail worth mentioning is that the instance must keep track of whether it has been closed: the close method must record in a field that the object is no longer valid, and other methods must check this field and throw an IllegalStateException if they are called after the object has been closed. -那么,如果一个类的对象封装了需要终止的资源,例如文件或线程,那么应该做什么,而不是为它编写终结器或清除器呢?只有你的类实现 AutoCloseable,要求其客户端每个实例在不再需要时调用关闭方法,通常使用 try-with-resources 确保终止,即使面对异常([Item-9](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2-Item-9-Prefer-try-with-resources-to-try-finally.md))。一个值得一提的细节是实例必须跟踪是否已经关闭:close 方法必须在字段中记录对象不再有效,其他方法必须检查这个字段,如果在对象关闭后调用它们,则必须抛出一个 IllegalStateException。 +那么,如果一个类的对象封装了需要终止的资源,例如文件或线程,那么应该做什么,而不是为它编写终结器或清除器呢?只有你的类实现 AutoCloseable,要求其客户端每个实例在不再需要时调用关闭方法,通常使用 try-with-resources 确保终止,即使面对异常([Item-9](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2/Chapter-2-Item-9-Prefer-try-with-resources-to-try-finally.md))。一个值得一提的细节是实例必须跟踪是否已经关闭:close 方法必须在字段中记录对象不再有效,其他方法必须检查这个字段,如果在对象关闭后调用它们,则必须抛出一个 IllegalStateException。 So what, if anything, are cleaners and finalizers good for? They have perhaps two legitimate uses. One is to act as a safety net in case the owner of a resource neglects to call its close method. While there’s no guarantee that the cleaner or finalizer will run promptly (or at all), it is better to free the resource late than never if the client fails to do so. If you’re considering writing such a safety-net finalizer, think long and hard about whether the protection is worth the cost.Some Java library classes, such as FileInputStream,FileOutputStream, ThreadPoolExecutor, and java.sql.Connection, have finalizers that serve as safety nets. @@ -104,7 +104,7 @@ The static nested State class holds the resources that are required by the clean It is critical that a State instance does not refer to its Room instance. If it did, it would create a circularity that would prevent the Room instance from becoming eligible for garbage collection (and from being automatically cleaned).Therefore, State must be a static nested class because nonstatic nested classes contain references to their enclosing instances (Item 24). It is similarly inadvisable to use a lambda because they can easily capture references to enclosing objects. -状态实例不引用其 Room 实例是非常重要的。如果它这样做了,它将创建一个循环,以防止 Room 实例有资格进行垃圾收集(以及自动清理)。因此,状态必须是一个静态嵌套类,因为非静态嵌套类包含对其封闭实例的引用([Item-24](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-24-Favor-static-member-classes-over-nonstatic.md))。同样不建议使用 lambda,因为它们可以很容易地捕获对包围对象的引用。 +状态实例不引用其 Room 实例是非常重要的。如果它这样做了,它将创建一个循环,以防止 Room 实例有资格进行垃圾收集(以及自动清理)。因此,状态必须是一个静态嵌套类,因为非静态嵌套类包含对其封闭实例的引用([Item-24](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-24-Favor-static-member-classes-over-nonstatic.md))。同样不建议使用 lambda,因为它们可以很容易地捕获对包围对象的引用。 As we said earlier, Room’s cleaner is used only as a safety net. If clients surround all Room instantiations in try-with-resource blocks, automatic cleaning will never be required. This well-behaved client demonstrates that behavior: diff --git a/Chapter-2-Item-9-Prefer-try-with-resources-to-try-finally.md b/Chapter-2/Chapter-2-Item-9-Prefer-try-with-resources-to-try-finally.md similarity index 98% rename from Chapter-2-Item-9-Prefer-try-with-resources-to-try-finally.md rename to Chapter-2/Chapter-2-Item-9-Prefer-try-with-resources-to-try-finally.md index fc281f9..145e01e 100644 --- a/Chapter-2-Item-9-Prefer-try-with-resources-to-try-finally.md +++ b/Chapter-2/Chapter-2-Item-9-Prefer-try-with-resources-to-try-finally.md @@ -4,7 +4,7 @@ The Java libraries include many resources that must be closed manually(adv.手动地) by invoking a close method. Examples include InputStream,OutputStream, and java.sql.Connection. Closing resources is often overlooked(n.被忽视的;v.忽视) by clients, with predictably(adv.可以预见的是) dire performance consequences. While many of these resources use finalizers as a safety net, finalizers don’t work very well (Item 8). -Java库包含许多必须通过调用 close 方法手动关闭的资源。常见的有 InputStream、OutputStream 和 java.sql.Connection。关闭资源常常会被客户端忽略,这会导致可怕的性能后果。虽然这些资源中的许多都使用终结器作为安全网,但终结器并不能很好地工作([Item-8](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2-Item-8-Avoid-finalizers-and-cleaners.md))。 +Java库包含许多必须通过调用 close 方法手动关闭的资源。常见的有 InputStream、OutputStream 和 java.sql.Connection。关闭资源常常会被客户端忽略,这会导致可怕的性能后果。虽然这些资源中的许多都使用终结器作为安全网,但终结器并不能很好地工作([Item-8](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2/Chapter-2-Item-8-Avoid-finalizers-and-cleaners.md))。 Historically, a try-finally statement was the best way to guarantee that a resource would be closed properly, even in the face of an exception or return: diff --git a/Chapter-3-Introduction.md b/Chapter-3-Introduction.md deleted file mode 100644 index 8b7bff3..0000000 --- a/Chapter-3-Introduction.md +++ /dev/null @@ -1,11 +0,0 @@ -## Chapter 3. Methods Common to All Objects(对象的通用方法) - -### Chapter 3 Introduction(章节介绍) - -ALTHOUGH Object is a concrete class, it is designed primarily for extension.All of its nonfinal methods (equals, hashCode, toString, clone, and finalize) have explicit general contracts because they are designed to be overridden. It is the responsibility of any class overriding these methods to obey their general contracts; failure to do so will prevent other classes that depend on the contracts (such as HashMap and HashSet) from functioning properly in conjunction with the class. - -虽然Object是一个具体的类,但它主要是为扩展而设计的。它的所有非终态方法(equals、hashCode、toString、clone和finalize)都有显式的通用约定,因为它们的设计目的是被覆盖。任何类都有责任重写这些方法并将之作为一般约定;如果不这样做,将阻止依赖于约定的其他类(如HashMap和HashSet)与类一起正常工作。 - -This chapter tells you when and how to override the nonfinal Object methods. The finalize method is omitted from this chapter because it was discussed in Item 8. While not an Object method,Comparable.compareTo is discussed in this chapter because it has a similar character. - -本章将告诉你何时以及如何覆盖Object类的非终态方法。finalize方法在本章中被省略,因为它在[Item-8](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2-Item-8-Avoid-finalizers-and-cleaners.md)中讨论过。虽然Comparable.compareTo不是Object类的方法,但是它由于具有相似的特性,所以本章也对它进行讨论。 diff --git a/Chapter-3/Chapter-3-Introduction.md b/Chapter-3/Chapter-3-Introduction.md new file mode 100644 index 0000000..bd11970 --- /dev/null +++ b/Chapter-3/Chapter-3-Introduction.md @@ -0,0 +1,11 @@ +## Chapter 3. Methods Common to All Objects(对象的通用方法) + +### Chapter 3 Introduction(章节介绍) + +ALTHOUGH Object is a concrete class, it is designed primarily for extension. All of its nonfinal methods (equals, hashCode, toString, clone, and finalize) have explicit general contracts because they are designed to be overridden. It is the responsibility of any class overriding these methods to obey their general contracts; failure to do so will prevent other classes that depend on the contracts (such as HashMap and HashSet) from functioning properly in conjunction with the class. + +虽然 Object 是一个具体的类,但它主要是为扩展而设计的。它的所有非 final 方法(equals、hashCode、toString、clone 和 finalize)都有显式的通用约定,因为它们的设计目的是被覆盖。任何类都有责任覆盖这些方法并将之作为一般约定;如果不这样做,将阻止依赖于约定的其他类(如 HashMap 和 HashSet)与之一起正常工作。 + +This chapter tells you when and how to override the nonfinal Object methods. The finalize method is omitted from this chapter because it was discussed in Item 8. While not an Object method,Comparable.compareTo is discussed in this chapter because it has a similar character. + +本章将告诉你何时以及如何覆盖 Object 类的非 final 方法。finalize 方法在本章中被省略,因为它在 [Item-8](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2/Chapter-2-Item-8-Avoid-finalizers-and-cleaners.md) 中讨论过。虽然 Comparable.compareTo 不是 Object 类的方法,但是由于具有相似的特性,所以本章也对它进行讨论。 diff --git a/Chapter-3-Item-10-Obey-the-general-contract-when-overriding-equals.md b/Chapter-3/Chapter-3-Item-10-Obey-the-general-contract-when-overriding-equals.md similarity index 74% rename from Chapter-3-Item-10-Obey-the-general-contract-when-overriding-equals.md rename to Chapter-3/Chapter-3-Item-10-Obey-the-general-contract-when-overriding-equals.md index 531b253..a2320b4 100644 --- a/Chapter-3-Item-10-Obey-the-general-contract-when-overriding-equals.md +++ b/Chapter-3/Chapter-3-Item-10-Obey-the-general-contract-when-overriding-equals.md @@ -1,111 +1,152 @@ ## Chapter 3. Methods Common to All Objects(对象的通用方法) -### Item 10: Obey the general contract when overriding equals(重写equals方法时应遵守的约定) +### Item 10: Obey the general contract when overriding equals(覆盖 equals 方法时应遵守的约定) Overriding the equals method seems simple, but there are many ways to get it wrong, and consequences can be dire. The easiest way to avoid problems is not to override the equals method, in which case each instance of the class is equal only to itself. This is the right thing to do if any of the following conditions apply: -重写equals方法似乎很简单,但是有很多(重写的)方式会出错,而且后果可能非常严重。避免问题的最简单方法是不重写equals方法,在这种情况下,类的每个实例都只等于它自己。如果符合下列任何条件,这是正确的做法: +覆盖 equals 方法似乎很简单,但是有很多覆盖的方式会导致出错,而且后果可能非常严重。避免问题的最简单方法是不覆盖 equals 方法,在这种情况下,类的每个实例都只等于它自己。如果符合下列任何条件,就是正确的做法: - **Each instance of the class is inherently unique.** This is true for classes such as Thread that represent active entities rather than values. The equals implementation provided by Object has exactly the right behavior for these classes. -**类的每个实例本质上都是唯一的。** 对于像Thread这样表示活动实体类而不是值类来说也是如此。Object提供的equals实现对于这些类具有完全正确的行为。 +**类的每个实例本质上都是唯一的。** 对于像 Thread 这样表示活动实体类而不是值类来说也是如此。Object 提供的 equals 实现对于这些类具有完全正确的行为。 - **There is no need for the class to provide a “logical equality” test.** For example, java.util.regex.Pattern could have overridden equals to check whether two Pattern instances represented exactly the same regular expression, but the designers didn’t think that clients would need or want this functionality. Under these circumstances, the equals implementation inherited from Object is ideal. -**该类不需要提供“逻辑平等”测试。** 例如,java.util.regex.Pattern可以重写equals来检查两个模式实例是否表示完全相同的正则表达式,但设计人员认为客户端不需要或不需要这个功能。在这种情况下,从Object继承的equals实现是理想的。 +**该类不需要提供「逻辑相等」测试。** 例如,java.util.regex.Pattern 可以覆盖 equals 来检查两个 Pattern 实例是否表示完全相同的正则表达式,但设计人员认为客户端不需要或不需要这个功能。在这种情况下,从 Object 继承的 equals 实现是理想的。 - **A superclass has already overridden equals, and the superclass behavior is appropriate for this class.** For example, most Set implementations inherit their equals implementation from AbstractSet, List implementations from AbstractList, and Map implementations from AbstractMap. -**超类已经重写了equals,超类行为适合于这个类。** 例如,大多数Set的实现从AbstractSet继承其对等实现,List从AbstractList继承实现,Map从 AbstractMap继承实现。 +**超类已经覆盖了 equals,超类行为适合于这个类。** 例如,大多数 Set 的实现从 AbstractSet 继承其对等实现,List 从 AbstractList 继承实现,Map 从 AbstractMap 继承实现。 - **The class is private or package-private, and you are certain that its equals method will never be invoked.** If you are extremely risk-averse,you can override the equals method to ensure that it isn’t invoked accidentally: -**类是私有的或包私有的,并且您确信它的equals方法永远不会被调用。** 如果您非常厌恶风险,您可以覆盖equals方法,以确保它不会意外调用: +**类是私有的或包私有的,并且你确信它的 equals 方法永远不会被调用。** 如果你非常厌恶风险,你可以覆盖 equals 方法,以确保它不会意外调用: ``` -@Override public boolean equals(Object o) { +@Override +public boolean equals(Object o) { throw new AssertionError(); // Method is never called } ``` So when is it appropriate to override equals? It is when a class has a notion of logical equality that differs from mere object identity and a superclass has not already overridden equals. This is generally the case for value classes. A value class is simply a class that represents a value, such as Integer or String. A programmer who compares references to value objects using the equals method expects to find out whether they are logically equivalent, not whether they refer to the same object. Not only is overriding the equals method necessary to satisfy programmer expectations, it enables instances to serve as map keys or set elements with predictable, desirable behavior. -什么时候重写等于是合适的?它是当一个类具有与纯粹的对象标识不同的逻辑平等概念,并且超类还没有覆盖equals时。对于值类通常是这样。值类只是表示值的类,例如整数或字符串。使用equals方法将引用与值对象进行比较的程序员希望知道它们在逻辑上是否等价,而不是它们是否引用同一对象。它不仅覆盖了满足程序员期望所必需的equals方法,还允许实例充当映射键或设置具有可预测的、理想行为的元素。 +什么时候覆盖 equals 方法是合适的?当一个类有一个逻辑相等的概念,而这个概念不同于仅判断对象的同一性(相同对象的引用),并且超类还没有覆盖 equals。对于值类通常是这样。值类只是表示值的类,例如 Integer 或 String。使用 equals 方法比较引用和值对象的程序员希望发现它们在逻辑上是否等价,而不是它们是否引用相同的对象。覆盖 equals 方法不仅是为了满足程序员的期望,它还使实例能够作为 Map 的键或 Set 元素时,具有可预测的、理想的行为。 + +***译注1 :有一个表示状态的内部类。没有覆盖 equals 方法时,equals 的结果与 s1==s2 相同,为 false,即两者并不是相同对象的引用。*** +``` +public static void main(String[] args) { + + class Status { + public String status; + } + + Status s1 = new Status(); + Status s2 = new Status(); + + System.out.println(s1==s2); // false + System.out.println(s1.equals(s2)); // false +} +``` +***译注2 :覆盖 equals 方法后,以业务逻辑来判断是否相同,具备相同 status 字段即为相同。在使用去重功能时,也以此作为判断依据。*** +``` +public static void main(String[] args) { + + class Status { + public String status; + + @Override + public boolean equals(Object o) { + return Objects.equals(status, ((Status) o).status); + } + } + + Status s1 = new Status(); + Status s2 = new Status(); + + System.out.println(s1==s2); // false + System.out.println(s1.equals(s2)); // true +} +``` One kind of value class that does not require the equals method to be overridden is a class that uses instance control (Item 1) to ensure that at most one object exists with each value. Enum types (Item 34) fall into this category. For these classes, logical equality is the same as object identity, so Object’s equals method functions as a logical equals method. -不需要覆盖equals方法的一种值类是使用实例控件([Item-1](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2-Item-1-Consider-static-factory-methods-instead-of-constructors.md))来确保每个值最多只存在一个对象的类。枚举类型([Item-34](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-6-Item-34-Use-enums-instead-of-int-constants.md))属于这一类。对于这些类,逻辑等式与对象标识相同,因此对象的equals方法函数与逻辑equals方法相同。 +不需要覆盖 equals 方法的一种值类是使用实例控件([Item-1](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2/Chapter-2-Item-1-Consider-static-factory-methods-instead-of-constructors.md))来确保每个值最多只存在一个对象的类。枚举类型([Item-34](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-6/Chapter-6-Item-34-Use-enums-instead-of-int-constants.md))属于这一类。对于这些类,逻辑相等与对象标识相同,因此对象的 equals 方法函数与逻辑 equals 方法相同。 When you override the equals method, you must adhere to its general contract. Here is the contract, from the specification(n.规范,说明书) for Object : -当您重写equals方法时,您必须遵守它的通用契约。以下是合同,来自对象规范: +当你覆盖 equals 方法时,你必须遵守它的通用契约。以下是具体内容,来自 Object 规范: The equals method implements an equivalence relation. It has these properties: -equals方法实现了等价关系。它有这些属性: +equals方法实现了等价关系。它应有这些属性: - Reflexive: For any non-null reference value x, x.equals(x) must return true. -反身性:对于任何非空的参考值x,x.equals(x)必须返回true。 +反身性:对于任何非空的参考值 x,x.equals(x) 必须返回 true。 - Symmetric: For any non-null reference values x and y, x.equals(y) must return true if and only if y.equals(x) returns true. -对称性:对于任何非空参考值x和y,x.equals(y)必须在且仅当y.equals(x)返回true时返回true。 +对称性:对于任何非空参考值 x 和 y,x.equals(y) 必须在且仅当 y.equals(x) 返回 true 时返回 true。 - Transitive: For any non-null reference values x, y, z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) must return true. -传递性:对于任何非空的引用值x, y, z,如果x.equals(y)返回真,y.equals(z)返回真,那么x.equals(z)必须返回真。 +传递性:对于任何非空的引用值 x, y, z,如果 x.equals(y) 返回 true,y.equals(z) 返回 true,那么 x.equals(z) 必须返回 true。 - Consistent: For any non-null reference values x and y, multiple invocations of x.equals(y) must consistently return true or consistently return false, provided no information used in equals comparisons is modified. -一致性:对于任何非空的引用值x和y, x.equals(y)的多次调用必须一致地返回真值或一致地返回假值,前提是不修改等号比较中使用的信息。 +一致性:对于任何非空的引用值 x 和 y, x.equals(y) 的多次调用必须一致地返回 true 或一致地返回 false,前提是不修改 equals 中使用的信息。 - For any non-null reference value x, x.equals(null) must return false. -对于任何非空引用值x,x.equals(null)必须返回false。 +对于任何非空引用值 x,x.equals(null) 必须返回 false。 Unless you are mathematically inclined(v.使…倾向;adj.趋向于…的), this might look a bit scary, but do not ignore it! If you violate it, you may well find that your program behaves erratically or crashes, and it can be very difficult to pin down the source of the failure. To paraphrase John Donne, no class is an island. Instances of one class are frequently passed to another. Many classes, including all collections classes,depend on the objects passed to them obeying the equals contract. -除非你有数学倾向,否则这看起来有点可怕,但不要忽略它!如果您违反了它,您很可能会发现您的程序行为异常或崩溃,并且很难确定失败的根源。用约翰•多恩的话来说,没有一个类是孤立的。一个类的实例经常被传递给另一个类。许多类(包括所有集合类)依赖于传递给它们的对象遵守equals约定。 +除非你有数学方面的倾向,否则这些起来有点可怕,但不要忽略它!如果你违反了它,你的程序很可能会出现行为异常或崩溃,并且很难确定失败的根源。用 John Donne 的话来说,没有一个类是孤立的。一个类的实例经常被传递给另一个类。许多类(包括所有集合类)依赖于传递给它们的对象遵守 equals 约定。 Now that you are aware of the dangers of violating the equals contract, let’s go over the contract in detail. The good news is that, appearances notwithstanding, it really isn’t very complicated. Once you understand it, it’s not hard to adhere to it. -既然您已经意识到了违反equals约定的危险,让我们详细检查一下。好消息是,尽管表面上看起来很复杂,但其实并不复杂。一旦你明白了,就不难坚持下去了。 +既然你已经意识到了违反 equals 约定的危险,让我们详细讨论一下。好消息是,尽管表面上看起来很复杂,但其实并不复杂。一旦你明白了,就不难坚持下去了。 So what is an equivalence relation? Loosely speaking, it’s an operator that partitions a set of elements into subsets whose elements are deemed equal to one another. These subsets are known as equivalence classes. For an equals method to be useful, all of the elements in each equivalence class must be interchangeable from the perspective of the user. Now let’s examine the five requirements in turn: -什么是等价关系?简单地说,它是一个操作符,它将一组元素划分为子集,这些子集的元素被认为是相等的。这些子集被称为等价类。为了使equals方法有用,从用户的角度来看,每个等价类中的所有元素都必须是可互换的。现在让我们依次检查以下五个需求: +什么是等价关系?简单地说,它是一个操作符,它将一组元素划分为子集,子集的元素被认为是彼此相等的。这些子集被称为等价类。为了使 equals 方法有用,从用户的角度来看,每个等价类中的所有元素都必须是可互换的。现在让我们依次检查以下五个需求: **Reflexivity** —The first requirement says merely that an object must be equal to itself. It’s hard to imagine violating this one unintentionally. If you were to violate it and then add an instance of your class to a collection, the contains method might well say that the collection didn’t contain the instance that you just added. -**反身性** ,第一个要求仅仅是说一个对象必须等于它自己。很难想象无意中违反了这条规则。如果您违反了它,然后将类的一个实例添加到集合中,contains方法很可能会说该集合不包含您刚才添加的实例。 +**反身性** ,第一个要求仅仅是说一个对象必须等于它自己。很难想象会无意中违反了这条规则。如果你违反了它,然后将类的一个实例添加到集合中,contains 方法很可能会说该集合不包含你刚才添加的实例。 **Symmetry** —The second requirement says that any two objects must agree on whether they are equal. Unlike the first requirement, it’s not hard to imagine violating this one unintentionally. For example, consider the following class,which implements a case-insensitive string. The case of the string is preserved by toString but ignored in equals comparisons: -**对称性** ,第二个要求是任何两个物体必须在是否相等的问题上达成一致。与第一个要求不同,不难想象无意中违反了这个要求。例如,考虑下面的类,它实现了不区分大小写的字符串。字符串的情况是保留的toString,但忽略在equals的比较: +**对称性** ,第二个要求是任何两个对象必须在是否相等的问题上达成一致。与第一个要求不同,无意中违反了这个要求的情况不难想象。例如,考虑下面的类,它实现了不区分大小写的字符串。字符串的情况是保留的 toString,但忽略在 equals 的比较: ``` // Broken - violates symmetry! public final class CaseInsensitiveString { -private final String s; -public CaseInsensitiveString(String s) { -this.s = Objects.requireNonNull(s); + private final String s; + + public CaseInsensitiveString(String s) { + this.s = Objects.requireNonNull(s); } + // Broken - violates symmetry! -@Override public boolean equals(Object o) { -if (o instanceof CaseInsensitiveString) -return s.equalsIgnoreCase( -((CaseInsensitiveString) o).s); -if (o instanceof String) // One-way interoperability! -return s.equalsIgnoreCase((String) o); -return false; -} ... // Remainder omitted +@Override +public boolean equals(Object o) { + if (o instanceof CaseInsensitiveString) + return s.equalsIgnoreCase(((CaseInsensitiveString) o).s); + + if (o instanceof String) // One-way interoperability! + return s.equalsIgnoreCase((String) o); + + return false; + } ... // Remainder omitted } ``` The well-intentioned equals method in this class naively attempts to interoperate with ordinary strings. Let’s suppose that we have one caseinsensitive string and one ordinary one: -这个类中善意的equals方法天真地尝试与普通字符串进行互操作。假设我们有一个不区分大小写的字符串和一个普通字符串: +这个类中的 equals 方法天真地尝试与普通字符串进行互操作。假设我们有一个不区分大小写的字符串和一个普通字符串: ``` CaseInsensitiveString cis = new CaseInsensitiveString("Polish"); @@ -114,68 +155,107 @@ String s = "polish"; As expected, cis.equals(s) returns true. The problem is that while the equals method in CaseInsensitiveString knows about ordinary strings, the equals method in String is oblivious to case-insensitive strings.Therefore, s.equals(cis) returns false, a clear violation of symmetry.Suppose you put a case-insensitive string into a collection: -正如预期的那样,cis.equals(s)返回true。问题是,虽然CaseInsensitiveString中的equals方法知道普通字符串,但是String中的equals方法对不区分大小写的字符串不敏感。因此,s.equals(cis)返回false,这明显违反了对称性。假设您将不区分大小写的字符串放入集合中: +正如预期的那样,cis.equals(s) 返回 true。问题是,虽然 CaseInsensitiveString 中的 equals 方法知道普通字符串,但是 String 中的 equals 方法对不区分大小写的字符串不知情。因此,s.equals(cis) 返回 false,这明显违反了对称性。假设你将不区分大小写的字符串放入集合中: ``` List list = new ArrayList<>(); list.add(cis); ``` -What does list.contains(s) return at this point? Who knows? In the current OpenJDK implementation, it happens to return false, but that’s just an implementation artifact. In another implementation, it could just as easily return true or throw a runtime exception. **Once you’ve violated the equals contract, you simply don’t know how other objects will behave when confronted with your object.** +What does list.contains(s) return at this point? Who knows? In the current OpenJDK implementation, it happens to return false, but that’s just an implementation artifact. In another implementation, it could just as easily return true or throw a runtime exception. **Once you’ve violated the equals contract, you simply don’t know how other objects will behave when confronted with your object.** + +此时 list.contains(s) 返回什么?谁知道呢?在当前的 OpenJDK 实现中,它碰巧返回 false,但这只是一个实现案例。在另一个实现中,它可以很容易地返回 true 或抛出运行时异常。一旦你违反了 equals 约定,就不知道当其他对象面对你的对象时,会如何表现。 + +***译注:contains 方法在 ArrayList 中的实现源码如下(省略了源码中的多行注释):*** +``` +// ArrayList 的大小 +private int size; + +// 保存 ArrayList 元素的容器,一个 Object 数组 +transient Object[] elementData; // non-private to simplify nested class access + +public boolean contains(Object o) { + return indexOf(o) >= 0; +} + +public int indexOf(Object o) { + return indexOfRange(o, 0, size); +} -此时list.contains(s)返回什么?谁知道呢?在当前的OpenJDK实现中,它碰巧返回false,但这只是一个实现工件。在另一个实现中,它可以很容易地返回true或抛出运行时异常。一旦你违反了equals约定,你就不知道当你面对你的对象时,其他对象会如何表现。 +int indexOfRange(Object o, int start, int end) { + Object[] es = elementData; + if (o == null) { + for (int i = start; i < end; i++) { + if (es[i] == null) { + return i; + } + } + } else { + for (int i = start; i < end; i++) { + if (o.equals(es[i])) { + return i; + } + } + } + return -1; +} +``` To eliminate the problem, merely remove the ill-conceived attempt to interoperate with String from the equals method. Once you do this, you can refactor the method into a single return statement: -为了消除这个问题,只需从equals方法中删除与String互操作的错误尝试。一旦你这样做了,你可以重构方法成一个单一的返回语句: +为了消除这个问题,只需从 equals 方法中删除与 String 互操作的错误尝试。一旦你这样做了,你可以重构方法为一个单一的返回语句: ``` -@Override public boolean equals(Object o) { -return o instanceof CaseInsensitiveString && -((CaseInsensitiveString) o).s.equalsIgnoreCase(s); +@Override +public boolean equals(Object o) { + return o instanceof CaseInsensitiveString && ((CaseInsensitiveString) o).s.equalsIgnoreCase(s); } ``` **Transitivity** —The third requirement of the equals contract says that if one object is equal to a second and the second object is equal to a third, then the first object must be equal to the third. Again, it’s not hard to imagine violating this requirement unintentionally. Consider the case of a subclass that adds a new value component to its superclass. In other words, the subclass adds a piece of information that affects equals comparisons. Let’s start with a simple immutable two-dimensional integer point class: -**传递性** ,对等合同的第三个要求是,如果一个对象等于第二个对象,而第二个对象等于第三个对象,那么第一个对象必须等于第三个对象。同样,不难想象无意中违反了这个要求。考虑向超类添加新值组件的子类的情况。换句话说,子类添加了一条影响相等比较的信息。让我们从一个简单的不可变二维整数点类开始: +**传递性** ,equals 约定的第三个要求是,如果一个对象等于第二个对象,而第二个对象等于第三个对象,那么第一个对象必须等于第三个对象。同样,无意中违反了这个要求的情况不难想象。考虑向超类添加新的值组件时,子类的情况。换句话说,子类添加了一条影响 equals 比较的信息。让我们从一个简单的不可变二维整数点类开始: ``` public class Point { -private final int x; -private final int y; -public Point(int x, int y) { -this.x = x; -this.y = y; -} -@Override public boolean equals(Object o) { -if (!(o instanceof Point)) -return false; -Point p = (Point)o; -return p.x == x && p.y == y; -} -... // Remainder omitted + private final int x; + private final int y; + + public Point(int x, int y) { + this.x = x; + this.y = y; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Point)) + return false; + Point p = (Point)o; + return p.x == x && p.y == y; + } + ... // Remainder omitted } ``` Suppose you want to extend this class, adding the notion of color to a point: -假设您想扩展这个类,在一点上添加颜色的概念: +假设你想扩展这个类,对一个点添加颜色的概念: ``` public class ColorPoint extends Point { -private final Color color; -public ColorPoint(int x, int y, Color color) { -super(x, y); -this.color = color; -} -... // Remainder omitted + private final Color color; + + public ColorPoint(int x, int y, Color color) { + super(x, y); + this.color = color; + } + ... // Remainder omitted } ``` How should the equals method look? If you leave it out entirely, the implementation is inherited from Point and color information is ignored in equals comparisons. While this does not violate the equals contract, it is clearly unacceptable. Suppose you write an equals method that returns true only if its argument is another color point with the same position and color: -equals方法应该是什么样子?如果您完全忽略它,则实现将从点继承而来,在对等比较中颜色信息将被忽略。虽然这并不违反equals约定,但显然是不可接受的。假设您编写了一个equals方法,该方法只在其参数为具有相同位置和颜色的另一个颜色点时返回true: +equals 方法应该是什么样子?如果你完全忽略它,则实现将从点继承而来,在对等比较中颜色信息将被忽略。虽然这并不违反equals约定,但显然是不可接受的。假设你编写了一个equals方法,该方法只在其参数为具有相同位置和颜色的另一个颜色点时返回true: ``` // Broken - violates symmetry! @@ -232,7 +312,7 @@ Also, this approach can cause infinite recursion: Suppose there are two subclass So what’s the solution? It turns out that this is a fundamental problem of equivalence relations in object-oriented languages.** There is no way to extend an instantiable class and add a value component while preserving the equals contract,** unless you’re willing to forgo the benefits of object-oriented abstraction. -那么解决方案是什么?这是面向对象语言中等价关系的一个基本问题。除非您愿意放弃面向对象抽象的好处,否则无法扩展一个可实例化的类并添加一个值组件,同时保留equals 约定。 +那么解决方案是什么?这是面向对象语言中等价关系的一个基本问题。除非你愿意放弃面向对象抽象的好处,否则无法扩展一个可实例化的类并添加一个值组件,同时保留equals 约定。 You may hear it said that you can extend an instantiable class and add a value component while preserving the equals contract by using a getClass test in place of the instanceof test in the equals method: @@ -264,7 +344,7 @@ return unitCircle.contains(p); While this may not be the fastest way to implement the functionality, it works fine. Suppose you extend Point in some trivial way that doesn’t add a value component, say, by having its constructor keep track of how many instances have been created: -虽然这可能不是实现功能的最快方法,但它工作得很好。假设您以一种不添加值组件的简单方式扩展了Point,例如,让它的构造函数跟踪创建了多少实例: +虽然这可能不是实现功能的最快方法,但它工作得很好。假设你以一种不添加值组件的简单方式扩展了Point,例如,让它的构造函数跟踪创建了多少实例: ``` public class CounterPoint extends Point { @@ -280,11 +360,11 @@ return counter.get(); } The Liskov substitution principle says that any important property of a type should also hold for all its subtypes so that any method written for the type should work equally well on its subtypes [Liskov87]. This is the formal statement of our earlier claim that a subclass of Point (such as CounterPoint) is still a Point and must act as one. But suppose we pass a CounterPoint to the onUnitCircle method. If the Point class uses a getClass-based equals method, the onUnitCircle method will return false regardless of the CounterPoint instance’s x and y coordinates. This is so because most collections, including the HashSet used by the onUnitCircle method, use the equals method to test for containment, and no CounterPoint instance is equal to any Point. If, however, you use a proper instanceof-based equals method on Point, the same onUnitCircle method works fine when presented with a CounterPoint instance. -Liskov替换原则指出,类型的任何重要属性都应该适用于所有子类型,因此为类型编写的任何方法都应该在其子类型上同样有效[Liskov87]。这是我们先前做的正式声明,即点的子类(如CounterPoint)仍然是一个点,并且必须作为一个点。但假设我们传递了一个CounterPoint给onUnitCircle方法。如果Point类使用基于getclass的equals方法,那么不管CounterPoint实例的x和y坐标如何,onUnitCircle方法都会返回false。这是因为大多数集合,包括onUnitCircle方法使用的HashSet,都使用equals方法来测试包含性,没有一个CounterPoint实例等于任何一个点。但是,如果您在Point上使用了正确的基于实例的equals方法,那么在提供对位实例时,相同的onUnitCircle方法就可以很好地工作。 +Liskov替换原则指出,类型的任何重要属性都应该适用于所有子类型,因此为类型编写的任何方法都应该在其子类型上同样有效[Liskov87]。这是我们先前做的正式声明,即点的子类(如CounterPoint)仍然是一个点,并且必须作为一个点。但假设我们传递了一个CounterPoint给onUnitCircle方法。如果Point类使用基于getclass的equals方法,那么不管CounterPoint实例的x和y坐标如何,onUnitCircle方法都会返回false。这是因为大多数集合,包括onUnitCircle方法使用的HashSet,都使用equals方法来测试包含性,没有一个CounterPoint实例等于任何一个点。但是,如果你在Point上使用了正确的基于实例的equals方法,那么在提供对位实例时,相同的onUnitCircle方法就可以很好地工作。 While there is no satisfactory way to extend an instantiable class and add a value component, there is a fine workaround: Follow the advice of Item 18,“Favor composition over inheritance.” Instead of having ColorPoint extend Point, give ColorPoint a private Point field and a public view method (Item 6) that returns the point at the same position as this color point: -虽然没有令人满意的方法来扩展一个可实例化的类并添加一个值组件,但是有一个很好的解决方案:遵循[Item-18](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-18-Favor-composition-over-inheritance.md)的建议,“优先组合而不是继承”。与其使用着色点扩展点,不如给着色点一个私有的点字段和一个公共的视图方法([Item-6](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2-Item-6-Avoid-creating-unnecessary-objects.md)),它返回与这个颜色点处于同一位置的点: +虽然没有令人满意的方法来扩展一个可实例化的类并添加一个值组件,但是有一个很好的解决方案:遵循[Item-18](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-18-Favor-composition-over-inheritance.md)的建议,“优先组合而不是继承”。与其使用着色点扩展点,不如给着色点一个私有的点字段和一个公共的视图方法([Item-6](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2/Chapter-2-Item-6-Avoid-creating-unnecessary-objects.md)),它返回与这个颜色点处于同一位置的点: ``` // Adds a value component without violating the equals contract @@ -313,15 +393,15 @@ return cp.point.equals(point) && cp.color.equals(color); There are some classes in the Java platform libraries that do extend an instantiable class and add a value component. For example,java.sql.Timestamp extends java.util.Date and adds a nanoseconds field. The equals implementation for Timestamp does violate symmetry and can cause erratic behavior if Timestamp and Date objects are used in the same collection or are otherwise intermixed. The Timestamp class has a disclaimer cautioning programmers against mixing dates and timestamps. While you won’t get into trouble as long as you keep them separate, there’s nothing to prevent you from mixing them, and the resulting errors can be hard to debug. This behavior of the Timestamp class was a mistake and should not be emulated. -Java平台库中有一些类确实扩展了一个可实例化的类并添加了一个值组件。例如,java.sql.Timestamp扩展了java.util.Date并添加了纳秒字段。如果在同一个集合中使用时间戳和日期对象,或者以其他方式混合使用时间戳和日期对象,那么时间戳的equals实现确实违反了对称性,并且可能导致不稳定的行为。Timestamp类有一个免责声明,警告程序员不要混合使用日期和时间戳。虽然只要将它们分开,就不会遇到麻烦,但是没有什么可以阻止您将它们混合在一起,因此产生的错误可能很难调试。时间戳类的这种行为是错误的,不应该效仿。 +Java平台库中有一些类确实扩展了一个可实例化的类并添加了一个值组件。例如,java.sql.Timestamp扩展了java.util.Date并添加了纳秒字段。如果在同一个集合中使用时间戳和日期对象,或者以其他方式混合使用时间戳和日期对象,那么时间戳的equals实现确实违反了对称性,并且可能导致不稳定的行为。Timestamp类有一个免责声明,警告程序员不要混合使用日期和时间戳。虽然只要将它们分开,就不会遇到麻烦,但是没有什么可以阻止你将它们混合在一起,因此产生的错误可能很难调试。时间戳类的这种行为是错误的,不应该效仿。 Note that you can add a value component to a subclass of an abstract class without violating the equals contract. This is important for the sort of class hierarchies that you get by following the advice in Item 23, “Prefer class hierarchies to tagged classes.” For example, you could have an abstract class Shape with no value components, a subclass Circle that adds a radius field, and a subclass Rectangle that adds length and width fields.Problems of the sort shown earlier won’t occur so long as it is impossible to create a superclass instance directly. -注意,您可以向抽象类的子类添加一个值组件,而不违反equals约定。这对于遵循[Item-23](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-23-Prefer-class-hierarchies-to-tagged-classes.md)中的建议而得到的类层次结构很重要,“更喜欢类层次结构而不是标记类”。例如,可以有一个没有值组件的抽象类形状、一个添加半径字段的子类圆和一个添加长度和宽度字段的子类矩形。只要不可能直接创建超类实例,前面显示的那种问题就不会发生。 +注意,你可以向抽象类的子类添加一个值组件,而不违反equals约定。这对于遵循[Item-23](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-23-Prefer-class-hierarchies-to-tagged-classes.md)中的建议而得到的类层次结构很重要,“更喜欢类层次结构而不是标记类”。例如,可以有一个没有值组件的抽象类形状、一个添加半径字段的子类圆和一个添加长度和宽度字段的子类矩形。只要不可能直接创建超类实例,前面显示的那种问题就不会发生。 **Consistency—** The fourth requirement of the equals contract says that if two objects are equal, they must remain equal for all time unless one (or both) of them is modified. In other words, mutable objects can be equal to different objects at different times while immutable objects can’t. When you write a class,think hard about whether it should be immutable (Item 17). If you conclude that it should, make sure that your equals method enforces the restriction that equal objects remain equal and unequal objects remain unequal for all time. -**一致性** ,对等契约的第四个要求是,如果两个对象相等,它们必须一直保持相等,除非其中一个(或两个)被修改。换句话说,可变对象可以等于不同时间的不同对象,而不可变对象不能。在编写类时,仔细考虑它是否应该是不可变的([Item-17](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-17-Minimize-mutability.md))。如果您认为应该这样做,那么请确保您的equals方法执行了这样的限制,即相等的对象始终是相等的,而不等的对象始终是不等的。 +**一致性** ,对等契约的第四个要求是,如果两个对象相等,它们必须一直保持相等,除非其中一个(或两个)被修改。换句话说,可变对象可以等于不同时间的不同对象,而不可变对象不能。在编写类时,仔细考虑它是否应该是不可变的([Item-17](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-17-Minimize-mutability.md))。如果你认为应该这样做,那么请确保你的equals方法执行了这样的限制,即相等的对象始终是相等的,而不等的对象始终是不等的。 Whether or not a class is immutable, **do not write an equals method that depends on unreliable resources.** It’s extremely difficult to satisfy the consistency requirement if you violate this prohibition. For example,java.net.URL’s equals method relies on comparison of the IP addresses of the hosts associated with the URLs. Translating a host name to an IP address can require network access, and it isn’t guaranteed to yield the same results over time. This can cause the URL equals method to violate the equals contract and has caused problems in practice. The behavior of URL’s equals method was a big mistake and should not be emulated. Unfortunately, it cannot be changed due to compatibility requirements. To avoid this sort of problem,equals methods should perform only deterministic computations on memoryresident objects. @@ -374,11 +454,11 @@ Putting it all together, here’s a recipe for a high-quality equals method: 4、**For each “significant” field in the class, check if that field of the argument matches the corresponding field of this object.** If all these tests succeed, return true; otherwise, return false. If the type in Step 2 is an interface, you must access the argument’s fields via interface methods; if the type is a class, you may be able to access the fields directly, depending on their accessibility. -**对于类中的每个“重要”字段,检查参数的字段是否与该对象的相应字段匹配。** 如果所有这些测试都成功,返回true;否则返回false。如果第2步中的类型是接口,则必须通过接口方法访问参数的字段;如果类型是类,您可以根据字段的可访问性直接访问它们。 +**对于类中的每个“重要”字段,检查参数的字段是否与该对象的相应字段匹配。** 如果所有这些测试都成功,返回true;否则返回false。如果第2步中的类型是接口,则必须通过接口方法访问参数的字段;如果类型是类,你可以根据字段的可访问性直接访问它们。 For primitive fields whose type is not float or double, use the == operator for comparisons; for object reference fields, call the equals method recursively; for float fields, use the static Float.compare(float,float) method; and for double fields, use Double.compare(double, double). The special treatment of float and double fields is made necessary by the existence of Float.NaN, -0.0f and the analogous double values; see JLS 15.21.1 or the documentation of Float.equals for details. While you could compare float and double fields with the static methods Float.equals and Double.equals, this would entail autoboxing on every comparison, which would have poor performance. For array fields, apply these guidelines to each element. If every element in an array field is significant, use one of the Arrays.equals methods. -对于类型不是float或double的原始字段,使用==运算符进行比较;对于对象引用字段,递归调用equals方法;对于float字段,使用static Float.compare(float,float)方法;对于double字段,使用Double.compare(double, double)。float和double字段的特殊处理是由于Float.NaN、-0.0f和类似的双重值的存在而必须的;请参阅JLS 15.21.1或Float.equals文档。虽然您可以将float和double字段与静态方法Float.equals和Double.equals进行比较,这将需要在每个比较上进行自动装箱,这将有较差的性能。对于数组字段,将这些指导原则应用于每个元素。如果数组字段中的每个元素都很重要,那么使用Arrays.equals方法之一。 +对于类型不是float或double的原始字段,使用==运算符进行比较;对于对象引用字段,递归调用equals方法;对于float字段,使用static Float.compare(float,float)方法;对于double字段,使用Double.compare(double, double)。float和double字段的特殊处理是由于Float.NaN、-0.0f和类似的双重值的存在而必须的;请参阅JLS 15.21.1或Float.equals文档。虽然你可以将float和double字段与静态方法Float.equals和Double.equals进行比较,这将需要在每个比较上进行自动装箱,这将有较差的性能。对于数组字段,将这些指导原则应用于每个元素。如果数组字段中的每个元素都很重要,那么使用Arrays.equals方法之一。 Some object reference fields may legitimately contain null. To avoid the possibility of a NullPointerException, check such fields for equality using the static method Objects.equals(Object, Object). @@ -386,15 +466,15 @@ Some object reference fields may legitimately contain null. To avoid the possibi For some classes, such as CaseInsensitiveString above, field comparisons are more complex than simple equality tests. If this is the case,you may want to store a canonical form of the field so the equals method can do a cheap exact comparison on canonical forms rather than a more costly nonstandard comparison. This technique is most appropriate for immutable classes (Item 17); if the object can change, you must keep the canonical form up to date. -对于某些类,例如上面的CaseInsensitiveString,字段比较比简单的equal测试更复杂。如果是这样,您可能希望存储字段的规范形式,以便equals方法可以对规范形式进行廉价的精确比较,而不是更昂贵的非标准比较。这种技术最适合于不可变类([Item-17](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-17-Minimize-mutability.md));如果对象可以更改,则必须使规范形式保持最新。 +对于某些类,例如上面的CaseInsensitiveString,字段比较比简单的equal测试更复杂。如果是这样,你可能希望存储字段的规范形式,以便equals方法可以对规范形式进行廉价的精确比较,而不是更昂贵的非标准比较。这种技术最适合于不可变类([Item-17](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-17-Minimize-mutability.md));如果对象可以更改,则必须使规范形式保持最新。 The performance of the equals method may be affected by the order in which fields are compared. For best performance, you should first compare fields that are more likely to differ, less expensive to compare, or, ideally,both. You must not compare fields that are not part of an object’s logical state,such as lock fields used to synchronize operations. You need not compare derived fields, which can be calculated from “significant fields,” but doing so may improve the performance of the equals method. If a derived field amounts to a summary description of the entire object, comparing this field will save you the expense of comparing the actual data if the comparison fails.For example, suppose you have a Polygon class, and you cache the area. If two polygons have unequal areas, you needn’t bother comparing their edges and vertices. -equals方法的性能可能会受到字段比较顺序的影响。为了获得最佳性能,您应该首先比较那些更可能不同、比较成本更低的字段,或者理想情况下两者都比较。不能比较不属于对象逻辑状态的字段,例如用于同步操作的锁字段。您不需要比较派生字段(可以从“重要字段”计算),但是这样做可能会提高equals方法的性能。如果派生字段相当于整个对象的摘要描述,那么如果比较失败,比较该字段将节省比较实际数据的开销。例如,假设你有一个多边形类,你缓存这个区域。如果两个多边形的面积不相等,你不需要比较它们的边和顶点。 +equals方法的性能可能会受到字段比较顺序的影响。为了获得最佳性能,你应该首先比较那些更可能不同、比较成本更低的字段,或者理想情况下两者都比较。不能比较不属于对象逻辑状态的字段,例如用于同步操作的锁字段。你不需要比较派生字段(可以从“重要字段”计算),但是这样做可能会提高equals方法的性能。如果派生字段相当于整个对象的摘要描述,那么如果比较失败,比较该字段将节省比较实际数据的开销。例如,假设你有一个多边形类,你缓存这个区域。如果两个多边形的面积不相等,你不需要比较它们的边和顶点。 **When you are finished writing your equals method, ask yourself three questions: Is it symmetric? Is it transitive? Is it consistent?** And don’t just ask yourself; write unit tests to check, unless you used AutoValue (page 49) to generate your equals method, in which case you can safely omit the tests. If the properties fail to hold, figure out why, and modify the equals method accordingly. Of course your equals method must also satisfy the other two properties (reflexivity and non-nullity), but these two usually take care of themselves. -**写完equals方法后,问自己三个问题:它具备对称性吗?具备传递性吗?具备一致性吗?** 不要问自己,要编写单元测试来检查,除非使用AutoValue(第49页)来生成equals方法,在这种情况下,您可以安全地省略测试。如果属性不能保持,请找出原因,并相应地修改equals方法。当然,equals方法还必须满足其他两个属性(反身性和非无效性),但这两个通常会自己处理。 +**写完equals方法后,问自己三个问题:它具备对称性吗?具备传递性吗?具备一致性吗?** 不要问自己,要编写单元测试来检查,除非使用AutoValue(第49页)来生成equals方法,在这种情况下,你可以安全地省略测试。如果属性不能保持,请找出原因,并相应地修改equals方法。当然,equals方法还必须满足其他两个属性(反身性和非无效性),但这两个通常会自己处理。 An equals method constructed according to the previous recipe(n.食谱,配方) is shown in this simplistic PhoneNumber class: @@ -430,13 +510,13 @@ Here are a few final caveats: 以下是一些最后的警告: -- **Always override hashCode when you override equals (Item 11).** +- **Always override hashCode when you override equals (Item 11).** -**当您重写equals时,也重写hashCode。**([Item-11](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3-Item-11-Always-override-hashCode-when-you-override-equals.md)) +**当你覆盖equals时,也覆盖hashCode。**([Item-11](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3/Chapter-3-Item-11-Always-override-hashCode-when-you-override-equals.md)) - **Don’t try to be too clever.** If you simply test fields for equality, it’s not hard to adhere to the equals contract. If you are overly aggressive in searching for equivalence, it’s easy to get into trouble. It is generally a bad idea to take any form of aliasing into account. For example, the File class shouldn’t attempt to equate symbolic links referring to the same file. Thankfully, it doesn’t. -**不要自作聪明。** 如果您只是为了(判断)相等性而测试字段,那么遵循equals约定并不困难。如果你在寻求对等方面过于激进,很容易陷入麻烦。一般来说,考虑到任何形式的混叠都不是一个好主意。例如,File类不应该尝试将引用同一文件的符号链接等同起来。值得庆幸的是,它不是。 +**不要自作聪明。** 如果你只是为了(判断)相等性而测试字段,那么遵循equals约定并不困难。如果你在寻求对等方面过于激进,很容易陷入麻烦。一般来说,考虑到任何形式的混叠都不是一个好主意。例如,File类不应该尝试将引用同一文件的符号链接等同起来。值得庆幸的是,它不是。 - **Don’t substitute another type for Object in the equals declaration.** It is not uncommon for a programmer to write an equals method that looks like this and then spend hours puzzling over why it doesn’t work properly: @@ -451,11 +531,11 @@ public boolean equals(MyClass o) { The problem is that this method does not override Object.equals,whose argument is of type Object, but overloads it instead (Item 52). It is unacceptable to provide such a “strongly typed” equals method even in addition to the normal one, because it can cause Override annotations in subclasses to generate false positives and provide a false sense of security. -这里的问题是这个方法并不覆盖Object.equals,它的参数是Object类型,而是重载它([Item-52](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-8-Item-52-Use-overloading-judiciously.md))。提供这样一个“强类型的”equals方法是不可接受的,即使是普通的方法,因为它会导致子类中的覆盖注释产生误报并提供错误的安全感。 +这里的问题是这个方法并不覆盖Object.equals,它的参数是Object类型,而是重载它([Item-52](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-8/Chapter-8-Item-52-Use-overloading-judiciously.md))。提供这样一个“强类型的”equals方法是不可接受的,即使是普通的方法,因为它会导致子类中的覆盖注释产生误报并提供错误的安全感。 Consistent use of the Override annotation, as illustrated throughout this item, will prevent you from making this mistake (Item 40). This equals method won’t compile, and the error message will tell you exactly what is wrong: -一致地使用Override注释,如本项目所示,将防止您犯此错误([Item-40](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-6-Item-40-Consistently-use-the-Override-annotation.md))。这个equals方法不会编译,错误消息会告诉你什么是错误的: +一致地使用Override注释,如本项目所示,将防止你犯此错误([Item-40](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-6/Chapter-6-Item-40-Consistently-use-the-Override-annotation.md))。这个equals方法不会编译,错误消息会告诉你什么是错误的: ``` // Still broken, but won’t compile @@ -466,7 +546,7 @@ Consistent use of the Override annotation, as illustrated throughout this item, Writing and testing equals (and hashCode) methods is tedious, and the resulting code is mundane. An excellent alternative to writing and testing these methods manually is to use Google’s open source AutoValue framework, which automatically generates these methods for you, triggered by a single annotation on the class . In most cases, the methods generated by AutoValue are essentially identical to those you’d write yourself. -编写和测试equals (和hashCode)方法很乏味,生成的代码也很单调。手动编写和测试这些方法的一个很好的替代方法是使用谷歌的开源AutoValue框架,它会自动为您生成这些方法,由类上的一个注释触发。在大多数情况下,AutoValue生成的方法与您自己编写的方法基本相同。 +编写和测试equals (和hashCode)方法很乏味,生成的代码也很单调。手动编写和测试这些方法的一个很好的替代方法是使用谷歌的开源AutoValue框架,它会自动为你生成这些方法,由类上的一个注释触发。在大多数情况下,AutoValue生成的方法与你自己编写的方法基本相同。 IDEs, too, have facilities to generate equals and hashCode methods, but the resulting source code is more verbose and less readable than code that uses AutoValue, does not track changes in the class automatically, and therefore requires testing. That said, having IDEs generate equals (and hashCode)methods is generally preferable to implementing them manually because IDEs do not make careless mistakes, and humans do. @@ -474,4 +554,4 @@ IDE也有生成equals和hashCode方法的功能,但是生成的源代码比使 In summary, don’t override the equals method unless you have to: in many cases, the implementation inherited from Object does exactly what you want.If you do override equals, make sure to compare all of the class’s significant fields and to compare them in a manner that preserves all five provisions of the equals contract. -总之,除非必须,否则不要重写equals方法:在许多情况下,从Object继承而来的实现正是您想要的。如果您确实重写了equals,那么一定要比较类的所有重要字段,并以保留equals约定的所有5项规定的方式进行比较。 +总之,除非必须,否则不要覆盖equals方法:在许多情况下,从Object继承而来的实现正是你想要的。如果你确实覆盖了equals,那么一定要比较类的所有重要字段,并以保留equals约定的所有5项规定的方式进行比较。 diff --git a/Chapter-3-Item-11-Always-override-hashCode-when-you-override-equals.md b/Chapter-3/Chapter-3-Item-11-Always-override-hashCode-when-you-override-equals.md similarity index 95% rename from Chapter-3-Item-11-Always-override-hashCode-when-you-override-equals.md rename to Chapter-3/Chapter-3-Item-11-Always-override-hashCode-when-you-override-equals.md index b225504..ba9394e 100644 --- a/Chapter-3-Item-11-Always-override-hashCode-when-you-override-equals.md +++ b/Chapter-3/Chapter-3-Item-11-Always-override-hashCode-when-you-override-equals.md @@ -1,6 +1,6 @@ ## Chapter 3. Methods Common to All Objects(对象的通用方法) -### Item 11: Always override hashCode when you override equals(当重写equals时,也重写hashCode) +### Item 11: Always override hashCode when you override equals(当覆盖 equals 时,也覆盖 hashCode) **You must override hashCode in every class that overrides equals.** If you fail to do so, your class will violate(vt.违反) the general contract for hashCode, which will prevent it from functioning properly(adv.适当地,正确地) in collections such as HashMap and HashSet. Here is the contract, adapted from the Object specification : @@ -20,7 +20,7 @@ **The key provision(n.规定,条款) that is violated when you fail to override hashCode is the second one: equal objects must have equal hash codes.** Two distinct instances may be logically equal according to a class’s equals method, but to Object’s hashCode method, they’re just two objects with nothing much in common. Therefore, Object’s hashCode method returns two seemingly random numbers instead of two equal numbers as required by the contract.For example, suppose you attempt to use instances of the PhoneNumber class from Item 10 as keys in a HashMap: -**当您无法覆盖hashCode时,违反的关键条款是第二个:相等的对象必须具有相等的散列代码。** 根据类的equals方法,两个不同的实例在逻辑上可能是相等的,但是对于对象的hashCode方法来说,它们只是两个没有什么共同之处的对象。因此,Object的hashCode方法返回两个看似随机的数字,而不是约定要求的两个相等的数字。例如,假设您尝试使用[Item-10](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3-Item-10-Obey-the-general-contract-when-overriding-equals.md)中的PhoneNumber类实例作为HashMap中的键: +**当您无法覆盖hashCode时,违反的关键条款是第二个:相等的对象必须具有相等的散列代码。** 根据类的equals方法,两个不同的实例在逻辑上可能是相等的,但是对于对象的hashCode方法来说,它们只是两个没有什么共同之处的对象。因此,Object的hashCode方法返回两个看似随机的数字,而不是约定要求的两个相等的数字。例如,假设您尝试使用[Item-10](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3/Chapter-3-Item-10-Obey-the-general-contract-when-overriding-equals.md)中的PhoneNumber类实例作为HashMap中的键: ``` Map m = new HashMap<>(); @@ -50,7 +50,7 @@ A good hash function tends to produce unequal hash codes for unequal instances. 1、Declare an int variable named result, and initialize it to the hash code c for the first significant field in your object, as computed in step 2.a. (Recall from Item 10 that a significant field is a field that affects equals comparisons.) -声明一个名为result的int变量,并将其初始化为对象中第一个重要字段的哈希代码c,如步骤2.a中计算的那样。(回想一下[Item-10](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3-Item-10-Obey-the-general-contract-when-overriding-equals.md)中的重要字段是影响相等比较的字段。) +声明一个名为result的int变量,并将其初始化为对象中第一个重要字段的哈希代码c,如步骤2.a中计算的那样。(回想一下[Item-10](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3/Chapter-3-Item-10-Obey-the-general-contract-when-overriding-equals.md)中的重要字段是影响相等比较的字段。) 2、For every remaining significant field f in your object, do the following: @@ -127,7 +127,7 @@ return Objects.hash(lineNum, prefix, areaCode); If a class is immutable and the cost of computing the hash code is significant,you might consider caching the hash code in the object rather than recalculating it each time it is requested. If you believe that most objects of this type will be used as hash keys, then you should calculate the hash code when the instance is created. Otherwise, you might choose to lazily initialize the hash code the first time hash-Code is invoked. Some care is required to ensure that the class remains thread-safe in the presence of a lazily initialized field (Item 83). Our PhoneNumber class does not merit this treatment, but just to show you how it’s done, here it is. Note that the initial value for the hashCode field (in this case, 0) should not be the hash code of a commonly created instance: -如果一个类是不可变的,并且计算哈希代码的成本非常高,那么您可以考虑在对象中缓存哈希代码,而不是在每次请求时重新计算它。如果您认为这种类型的大多数对象都将用作哈希键,那么您应该在创建实例时计算哈希代码。否则,您可能选择在第一次调用哈希代码时延迟初始化哈希代码。在一个延迟初始化的字段([Item-83](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-11-Item-83-Use-lazy-initialization-judiciously.md))的情况下,需要一些注意来确保该类仍然是线程安全的。我们的PhoneNumber类不值得进行这种处理,但只是为了向您展示它是如何实现的,在这里。注意,hashCode字段的初始值(在本例中为0)不应该是通常创建的实例的哈希代码: +如果一个类是不可变的,并且计算哈希代码的成本非常高,那么您可以考虑在对象中缓存哈希代码,而不是在每次请求时重新计算它。如果您认为这种类型的大多数对象都将用作哈希键,那么您应该在创建实例时计算哈希代码。否则,您可能选择在第一次调用哈希代码时延迟初始化哈希代码。在一个延迟初始化的字段([Item-83](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-11/Chapter-11-Item-83-Use-lazy-initialization-judiciously.md))的情况下,需要一些注意来确保该类仍然是线程安全的。我们的PhoneNumber类不值得进行这种处理,但只是为了向您展示它是如何实现的,在这里。注意,hashCode字段的初始值(在本例中为0)不应该是通常创建的实例的哈希代码: ``` // hashCode method with lazily initialized cached hash code @@ -157,4 +157,4 @@ This is not just a theoretical problem. Prior to Java 2, the String hash functio In summary, you must override hashCode every time you override equals,or your program will not run correctly. Your hashCode method must obey the general contract specified in Object and must do a reasonable job assigning unequal hash codes to unequal instances. This is easy to achieve, if slightly tedious, using the recipe on page 51. As mentioned in Item 10, the AutoValue framework provides a fine alternative to writing equals and hashCode methods manually, and IDEs also provide some of this functionality. -总之,每次重写equals时都必须重写hashCode,否则程序将无法正确运行。您的hashCode方法必须遵守Object中指定的通用约定,并且必须合理地将不相等的哈希代码分配给不相等的实例。这很容易实现,如果有点乏味,(可)使用第51页的方法。如[Item-10](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3-Item-10-Obey-the-general-contract-when-overriding-equals.md)所述,AutoValue框架提供了一种很好的替代手动编写equals和hashCode的方法,IDE也提供了这种功能。 +总之,每次重写equals时都必须重写hashCode,否则程序将无法正确运行。您的hashCode方法必须遵守Object中指定的通用约定,并且必须合理地将不相等的哈希代码分配给不相等的实例。这很容易实现,如果有点乏味,(可)使用第51页的方法。如[Item-10](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3/Chapter-3-Item-10-Obey-the-general-contract-when-overriding-equals.md)所述,AutoValue框架提供了一种很好的替代手动编写equals和hashCode的方法,IDE也提供了这种功能。 diff --git a/Chapter-3-Item-12-Always-override-toString.md b/Chapter-3/Chapter-3-Item-12-Always-override-toString.md similarity index 84% rename from Chapter-3-Item-12-Always-override-toString.md rename to Chapter-3/Chapter-3-Item-12-Always-override-toString.md index 36042a1..192bd71 100644 --- a/Chapter-3-Item-12-Always-override-toString.md +++ b/Chapter-3/Chapter-3-Item-12-Always-override-toString.md @@ -1,6 +1,6 @@ ## Chapter 3. Methods Common to All Objects(对象的通用方法) -### Item 12: Always override toString(总是重写toString方法) +### Item 12: Always override toString(总是覆盖 toString 方法) While Object provides an implementation of the toString method, the string that it returns is generally not what the user of your class wants to see. It consists of(由…组成) the class name followed by an “at” sign (@) and the unsigned hexadecimal representation of the hash code, for example,PhoneNumber@163b91. The general contract for toString says that the returned string should be “a concise but informative representation that is easy for a person to read.” While it could be argued that PhoneNumber@163b91 is concise and easy to read, it isn’t very informative when compared to 707-867-5309. The toString contract goes on to say, “It is recommended that all subclasses override this method.” Good advice, indeed! @@ -8,7 +8,7 @@ While Object provides an implementation of the toString method, the string that While it isn’t as critical(adj.至关重要的) as obeying the equals and hashCode contracts (Items 10 and 11), **providing a good toString implementation makes your class much more pleasant to use and makes systems using the class easier to debug.** The toString method is automatically invoked when an object is passed to println, printf, the string concatenation(n.连结) operator, or assert, or is printed by a debugger. Even if you never call toString on an object, others may. For example, a component that has a reference to your object may include the string representation of the object in a logged error message. If you fail to override toString, the message may be all but useless. -虽然它不如遵守equals和hashCode约定([Item-10](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3-Item-10-Obey-the-general-contract-when-overriding-equals.md)和[Item-11](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3-Item-11-Always-override-hashCode-when-you-override-equals.md))那么重要,但是**提供一个好的toString实现(能)使类更易于使用,使用该类的系统(也)更易于调试。** 当对象被传递给println、printf、字符串连接操作符或断言或由调试器打印时,将自动调用toString方法。即使您从来没有调用toString对象,其他人也可能(使用)。例如,有对象引用的组件可以在日志错误消息中包含对象的字符串表示。如果您未能重写toString,则该消息可能完全无用。 +虽然它不如遵守equals和hashCode约定([Item-10](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3/Chapter-3-Item-10-Obey-the-general-contract-when-overriding-equals.md)和[Item-11](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3/Chapter-3-Item-11-Always-override-hashCode-when-you-override-equals.md))那么重要,但是**提供一个好的toString实现(能)使类更易于使用,使用该类的系统(也)更易于调试。** 当对象被传递给println、printf、字符串连接操作符或断言或由调试器打印时,将自动调用toString方法。即使您从来没有调用toString对象,其他人也可能(使用)。例如,有对象引用的组件可以在日志错误消息中包含对象的字符串表示。如果您未能重写toString,则该消息可能完全无用。 If you’ve provided a good toString method for PhoneNumber,generating a useful diagnostic message is as easy as this: @@ -40,7 +40,7 @@ The disadvantage of specifying the format of the toString return value is that o **Whether or not you decide to specify the format, you should clearly document your intentions.** If you specify the format, you should do so precisely. For example, here’s a toString method to go with the PhoneNumber class in Item 11: -**无论您是否决定指定格式,您都应该清楚地记录您的意图。** 如果指定了格式,则应该精确地指定格式。例如,这里有一个toString方法用于[Item-11](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3-Item-11-Always-override-hashCode-when-you-override-equals.md)中的PhoneNumber类: +**无论您是否决定指定格式,您都应该清楚地记录您的意图。** 如果指定了格式,则应该精确地指定格式。例如,这里有一个toString方法用于[Item-11](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3/Chapter-3-Item-11-Always-override-hashCode-when-you-override-equals.md)中的PhoneNumber类: ``` /** @@ -86,11 +86,11 @@ Whether or not you specify the format, **provide programmatic access to the info It makes no sense to write a toString method in a static utility class (Item 4). Nor should you write a toString method in most enum types (Item 34) because Java provides a perfectly good one for you. You should, however, write a toString method in any abstract class whose subclasses share a common string representation. For example, the toString methods on most collection implementations are inherited from the abstract collection classes. -在静态实用程序类中编写toString方法是没有意义的([Item-4](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2-Item-4-Enforce-noninstantiability-with-a-private-constructor.md)),在大多数enum类型中也不应该编写toString方法([Item-34](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-6-Item-34-Use-enums-instead-of-int-constants.md)),因为Java为您提供了一个非常好的方法。但是,您应该在任何抽象类中编写toString方法,该类的子类共享公共的字符串表示形式。例如,大多数集合实现上的toString方法都继承自抽象集合类。 +在静态实用程序类中编写toString方法是没有意义的([Item-4](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2/Chapter-2-Item-4-Enforce-noninstantiability-with-a-private-constructor.md)),在大多数enum类型中也不应该编写toString方法([Item-34](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-6/Chapter-6-Item-34-Use-enums-instead-of-int-constants.md)),因为Java为您提供了一个非常好的方法。但是,您应该在任何抽象类中编写toString方法,该类的子类共享公共的字符串表示形式。例如,大多数集合实现上的toString方法都继承自抽象集合类。 Google’s open source AutoValue facility, discussed in Item 10, will generate a toString method for you, as will most IDEs. These methods are great for telling you the contents of each field but aren’t specialized to the meaning of the class. So, for example, it would be inappropriate to use an automatically generated toString method for our PhoneNumber class (as phone numbers have a standard string representation), but it would be perfectly acceptable for our Potion class. That said, an automatically generated toString method is far preferable to the one inherited from Object, which tells you nothing about an object’s value. -谷歌的开放源码自动值工具(在[Item-10](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3-Item-10-Obey-the-general-contract-when-overriding-equals.md)中讨论)将为您生成toString方法,大多数IDE也是如此。这些方法可以很好地告诉您每个字段的内容,但并不专门针对类的含义。因此,例如,对于PhoneNumber类使用自动生成的toString方法是不合适的(因为电话号码具有标准的字符串表示形式),但是对于Potion类来说它是完全可以接受的。也就是说,一个自动生成的toString方法要比从对象继承的方法好得多,对象继承的方法不会告诉你对象的值。 +谷歌的开放源码自动值工具(在[Item-10](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3/Chapter-3-Item-10-Obey-the-general-contract-when-overriding-equals.md)中讨论)将为您生成toString方法,大多数IDE也是如此。这些方法可以很好地告诉您每个字段的内容,但并不专门针对类的含义。因此,例如,对于PhoneNumber类使用自动生成的toString方法是不合适的(因为电话号码具有标准的字符串表示形式),但是对于Potion类来说它是完全可以接受的。也就是说,一个自动生成的toString方法要比从对象继承的方法好得多,对象继承的方法不会告诉你对象的值。 To recap, override Object’s toString implementation in every instantiable class you write, unless a superclass has already done so. It makes classes much more pleasant to use and aids in debugging. The toString method should return a concise, useful description of the object, in an aesthetically pleasing format. diff --git a/Chapter-3-Item-13-Override-clone-judiciously.md b/Chapter-3/Chapter-3-Item-13-Override-clone-judiciously.md similarity index 91% rename from Chapter-3-Item-13-Override-clone-judiciously.md rename to Chapter-3/Chapter-3-Item-13-Override-clone-judiciously.md index f8c39d7..c4c8a4a 100644 --- a/Chapter-3-Item-13-Override-clone-judiciously.md +++ b/Chapter-3/Chapter-3-Item-13-Override-clone-judiciously.md @@ -1,10 +1,10 @@ ## Chapter 3. Methods Common to All Objects(对象的通用方法) -### Item 13: Override clone judiciously(明智地重写clone方法) +### Item 13: Override clone judiciously(明智地覆盖 clone 方法) The Cloneable interface was intended(目的) as a mixin interface (Item 20) for classes to advertise that they permit cloning. Unfortunately, it fails to serve this purpose. Its primary flaw(n. 瑕疵,缺点) is that it lacks a clone method, and Object’s clone method is protected. You cannot, without resorting(求助) to reflection (Item 65), invoke clone on an object merely(adv. 仅仅,只是) because it implements Cloneable.Even a reflective invocation may fail, because there is no guarantee(n. 保证;担保) that the object has an accessible clone method. Despite this flaw and many others, the facility(n. 设施;设备) is in reasonably wide use, so it pays to understand it. This item tells you how to implement a well-behaved clone method, discusses when it is appropriate to do so, and presents alternatives. -Cloneable接口的目的是作为mixin接口([Item-20](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-20-Prefer-interfaces-to-abstract-classes.md)),用于让类来宣称它们允许克隆。不幸的是,它没有达到这个目的。它的主要缺点是缺少clone方法,并且Object类的clone方法是受保护的。如果不求助于反射([Item-65](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9-Item-65-Prefer-interfaces-to-reflection.md)),就不能仅仅因为对象实现了Cloneable就能调用clone方法。即使反射调用也可能失败,因为不能保证对象具有可访问的clone方法。尽管存在这样那样的缺陷,但该设施的使用范围相当广泛,因此理解它是值得的。本项目将告诉您如何实现行为良好的clone方法,讨论什么时候应该这样做,并提供替代方法。 +Cloneable接口的目的是作为mixin接口([Item-20](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-20-Prefer-interfaces-to-abstract-classes.md)),用于让类来宣称它们允许克隆。不幸的是,它没有达到这个目的。它的主要缺点是缺少clone方法,并且Object类的clone方法是受保护的。如果不求助于反射([Item-65](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9/Chapter-9-Item-65-Prefer-interfaces-to-reflection.md)),就不能仅仅因为对象实现了Cloneable就能调用clone方法。即使反射调用也可能失败,因为不能保证对象具有可访问的clone方法。尽管存在这样那样的缺陷,但该设施的使用范围相当广泛,因此理解它是值得的。本项目将告诉您如何实现行为良好的clone方法,讨论什么时候应该这样做,并提供替代方法。 **译注:mixin是掺合,混合,糅合的意思,即可以将任意一个对象的全部或部分属性拷贝到另一个对象上。** @@ -66,7 +66,7 @@ This mechanism is vaguely similar to constructor chaining, except that it isn’ Suppose you want to implement Cloneable in a class whose superclass provides a well-behaved clone method. First call super.clone. The object you get back will be a fully functional replica of the original. Any fields declared in your class will have values identical to those of the original. If every field contains a primitive value or a reference to an immutable object, the returned object may be exactly what you need, in which case no further processing is necessary. This is the case, for example, for the PhoneNumber class in Item 11, but note that **immutable classes should never provide a clone method** because it would merely encourage wasteful copying. With that caveat, here’s how a clone method for PhoneNumber would look: -假设您希望在一个类中实现Cloneable,该类的超类提供了一个表现良好的克隆方法。第一个叫super.clone。返回的对象将是原始对象的完整功能副本。类中声明的任何字段都具有与原始字段相同的值。如果每个字段都包含一个基元值或对不可变对象的引用,那么返回的对象可能正是您所需要的,在这种情况下不需要进一步的处理。例如,对于[Item-11](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3-Item-11-Always-override-hashCode-when-you-override-equals.md)中的PhoneNumber类就是这样,但是要注意,**不可变类永远不应该提供克隆方法**,因为它只会鼓励浪费复制。有了这个警告,以下是PhoneNumber的克隆方法的外观: +假设您希望在一个类中实现Cloneable,该类的超类提供了一个表现良好的克隆方法。第一个叫super.clone。返回的对象将是原始对象的完整功能副本。类中声明的任何字段都具有与原始字段相同的值。如果每个字段都包含一个基元值或对不可变对象的引用,那么返回的对象可能正是您所需要的,在这种情况下不需要进一步的处理。例如,对于[Item-11](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3/Chapter-3-Item-11-Always-override-hashCode-when-you-override-equals.md)中的PhoneNumber类就是这样,但是要注意,**不可变类永远不应该提供克隆方法**,因为它只会鼓励浪费复制。有了这个警告,以下是PhoneNumber的克隆方法的外观: ``` // Clone method for class with no references to mutable state @@ -85,11 +85,11 @@ In order for this method to work, the class declaration for PhoneNumber would ha The call to super.clone is contained in a try-catch block. This is because Object declares its clone method to throw CloneNotSupportedException, which is a checked exception. Because PhoneNumber implements Cloneable, we know the call to super.clone will succeed. The need for this boilerplate indicates that CloneNotSupportedException should have been unchecked (Item 71). -对super.clone的调用包含在try-catch块中。这是因为Object声明其克隆方法来抛出CloneNotSupportedException异常。因为PhoneNumber实现了Cloneable,所以我们知道对super.clone的调用将会成功。这个样板文件的需要表明CloneNotSupportedException应该是被选中的([Item-71](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-10-Item-71-Avoid-unnecessary-use-of-checked-exceptions.md))。 +对super.clone的调用包含在try-catch块中。这是因为Object声明其克隆方法来抛出CloneNotSupportedException异常。因为PhoneNumber实现了Cloneable,所以我们知道对super.clone的调用将会成功。这个样板文件的需要表明CloneNotSupportedException应该是被选中的([Item-71](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-10/Chapter-10-Item-71-Avoid-unnecessary-use-of-checked-exceptions.md))。 If an object contains fields that refer to mutable objects, the simple clone implementation shown earlier can be disastrous. For example, consider the Stack class in Item 7: -如果对象包含引用可变对象的字段,前面所示的简单克隆实现可能是灾难性的。例如,考虑[Item-7](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2-Item-7-Eliminate-obsolete-object-references.md)中的堆栈类: +如果对象包含引用可变对象的字段,前面所示的简单克隆实现可能是灾难性的。例如,考虑[Item-7](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2/Chapter-2-Item-7-Eliminate-obsolete-object-references.md)中的堆栈类: ``` public class Stack { @@ -254,11 +254,11 @@ Like a constructor, a clone method must never invoke an overridable method on th Object’s clone method is declared to throw CloneNotSupportedException, but overriding methods need not. **Public clone methods should omit the throws clause,** as methods that don’t throw checked exceptions are easier to use (Item 71). -对象的clone方法被声明为抛出CloneNotSupportedException异常,但是重写方法不需要。**公共克隆方法应该省略throw子句,** 作为不抛出受控异常的方法更容易使用([Item-71](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-10-Item-71-Avoid-unnecessary-use-of-checked-exceptions.md))。 +对象的clone方法被声明为抛出CloneNotSupportedException异常,但是重写方法不需要。**公共克隆方法应该省略throw子句,** 作为不抛出受控异常的方法更容易使用([Item-71](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-10/Chapter-10-Item-71-Avoid-unnecessary-use-of-checked-exceptions.md))。 You have two choices when designing a class for inheritance (Item 19), but whichever one you choose, the class should not implement Cloneable. You may choose to mimic the behavior of Object by implementing a properly functioning protected clone method that is declared to throw CloneNotSupportedException. This gives subclasses the freedom to implement Cloneable or not, just as if they extended Object directly.Alternatively, you may choose not to implement a working clone method, and to prevent subclasses from implementing one, by providing the following degenerate clone implementation: -在为继承设计类时,您有两种选择([Item-19](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-19-Design-and-document-for-inheritance-or-else-prohibit-it.md)),但是无论您选择哪一种,类都不应该实现Cloneable。您可以选择通过实现一个功能正常的受保护克隆方法来模拟对象的行为,该方法声明为抛出CloneNotSupportedException异常。这给子类实现Cloneable或不实现Cloneable的自由,就像它们直接扩展对象一样。或者,您可以选择不实现工作克隆方法,并通过提供以下简并克隆实现来防止子类实现一个工作克隆方法: +在为继承设计类时,您有两种选择([Item-19](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-19-Design-and-document-for-inheritance-or-else-prohibit-it.md)),但是无论您选择哪一种,类都不应该实现Cloneable。您可以选择通过实现一个功能正常的受保护克隆方法来模拟对象的行为,该方法声明为抛出CloneNotSupportedException异常。这给子类实现Cloneable或不实现Cloneable的自由,就像它们直接扩展对象一样。或者,您可以选择不实现工作克隆方法,并通过提供以下简并克隆实现来防止子类实现一个工作克隆方法: ``` // clone method for extendable class not supporting Cloneable @@ -270,7 +270,7 @@ protected final Object clone() throws CloneNotSupportedException { There is one more detail that bears noting. If you write a thread-safe class that implements Cloneable, remember that its clone method must be properly synchronized, just like any other method (Item 78). Object’s clone method is not synchronized, so even if its implementation is otherwise satisfactory, you may have to write a synchronized clone method that returns super.clone(). -还有一个细节需要注意。如果您编写了一个实现了Cloneable的线程安全类,请记住它的克隆方法必须正确同步,就像其他任何方法一样([Item-78](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-11-Item-78-Synchronize-access-to-shared-mutable-data.md))。对象的克隆方法不是同步的,因此即使它的实现在其他方面是令人满意的,您也可能需要编写一个返回super.clone()的同步克隆方法。 +还有一个细节需要注意。如果您编写了一个实现了Cloneable的线程安全类,请记住它的克隆方法必须正确同步,就像其他任何方法一样([Item-78](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-11/Chapter-11-Item-78-Synchronize-access-to-shared-mutable-data.md))。对象的克隆方法不是同步的,因此即使它的实现在其他方面是令人满意的,您也可能需要编写一个返回super.clone()的同步克隆方法。 To recap, all classes that implement Cloneable should override clone with a public method whose return type is the class itself. This method should first call super.clone, then fix any fields that need fixing. Typically, this means copying any mutable objects that comprise the internal “deep structure” of the object and replacing the clone’s references to these objects with references to their copies. While these internal copies can usually be made by calling clone recursively, this is not always the best approach. If the class contains only primitive fields or references to immutable objects, then it is likely the case that no fields need to be fixed. There are exceptions to this rule. For example, a field representing a serial number or other unique ID will need to be fixed even if it is primitive or immutable. @@ -287,7 +287,7 @@ public Yum(Yum yum) { ... }; A copy factory is the static factory (Item 1) analogue of a copy constructor: -复制工厂是复制构造函数的静态工厂([Item-1](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2-Item-1-Consider-static-factory-methods-instead-of-constructors.md))类似物: +复制工厂是复制构造函数的静态工厂([Item-1](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2/Chapter-2-Item-1-Consider-static-factory-methods-instead-of-constructors.md))类似物: ``` // Copy factory @@ -304,4 +304,4 @@ Furthermore, a copy constructor or factory can take an argument whose type is an Given all the problems associated(adj. 关联的;联合的) with Cloneable, new interfaces should not extend it, and new extendable classes should not implement it. While it’s less harmful for final classes to implement Cloneable, this should be viewed as a performance optimization, reserved for the rare cases where it is justified (Item 67). As a rule, copy functionality is best provided by constructors or factories. A notable exception to this rule is arrays, which are best copied with the clone method. -考虑到与Cloneable相关的所有问题,新的接口不应该扩展它,新的可扩展类不应该实现它。虽然最终类实现Cloneable的危害要小一些,但这应该被视为一种性能优化,仅在极少数情况下([Item-67](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9-Item-67-Optimize-judiciously.md))是合理的。通常,复制功能最好由构造函数或工厂提供。这个规则的一个明显的例外是数组,最好使用clone方法来复制数组。 +考虑到与Cloneable相关的所有问题,新的接口不应该扩展它,新的可扩展类不应该实现它。虽然最终类实现Cloneable的危害要小一些,但这应该被视为一种性能优化,仅在极少数情况下([Item-67](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9/Chapter-9-Item-67-Optimize-judiciously.md))是合理的。通常,复制功能最好由构造函数或工厂提供。这个规则的一个明显的例外是数组,最好使用clone方法来复制数组。 diff --git a/Chapter-3-Item-14-Consider-implementing-Comparable.md b/Chapter-3/Chapter-3-Item-14-Consider-implementing-Comparable.md similarity index 100% rename from Chapter-3-Item-14-Consider-implementing-Comparable.md rename to Chapter-3/Chapter-3-Item-14-Consider-implementing-Comparable.md diff --git a/Chapter-4-Introduction.md b/Chapter-4/Chapter-4-Introduction.md similarity index 100% rename from Chapter-4-Introduction.md rename to Chapter-4/Chapter-4-Introduction.md diff --git a/Chapter-4-Item-15-Minimize-the-accessibility-of-classes-and-members.md b/Chapter-4/Chapter-4-Item-15-Minimize-the-accessibility-of-classes-and-members.md similarity index 87% rename from Chapter-4-Item-15-Minimize-the-accessibility-of-classes-and-members.md rename to Chapter-4/Chapter-4-Item-15-Minimize-the-accessibility-of-classes-and-members.md index 0ff906e..f5427be 100644 --- a/Chapter-4-Item-15-Minimize-the-accessibility-of-classes-and-members.md +++ b/Chapter-4/Chapter-4-Item-15-Minimize-the-accessibility-of-classes-and-members.md @@ -8,7 +8,7 @@ The single most important factor that distinguishes(vt/vi. 区别,区分; Information hiding is important for many reasons, most of which stem from the fact that it decouples(解耦) the components that comprise a system, allowing them to be developed, tested, optimized, used, understood, and modified in isolation(n. 隔离;孤立).This speeds up system development because components can be developed in parallel(n. 平行线;对比 adj. 平行的;类似的,相同的 vt. 使…与…平行). It eases(简化了) the burden(n. 负担;责任;vt. 使负担;烦扰) of maintenance(n. 维护,维修;保持) because components can be understood more quickly and debugged or replaced with little fear of harming other components. While information hiding does not, in and of itself, cause good performance, it enables effective performance tuning: once a system is complete and profiling(剖析) has determined which components are causing performance problems (Item 67), those components can be optimized without affecting the correctness of others. Information hiding increases software reuse because components that aren’t tightly coupled often prove useful in other contexts besides the ones for which they were developed. Finally, information hiding decreases the risk in building large systems because individual components may prove successful even if the system does not. -由于许多原因,信息隐藏是重要的,其中大部分原因源于这样一个事实:它解耦了组成系统的组件,允许它们被单独开发、测试、优化、使用、理解和修改。这加速了系统开发,因为组件可以并行开发。它减轻了维护的负担,因为组件可以被更快地理解、调试或替换,而不必担心会损害其他组件。虽然信息隐藏本身不会导致良好的性能,但它能够进行有效的性能调优:一旦系统完成,概要分析确定了哪些组件会导致性能问题([Item-67](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9-Item-67-Optimize-judiciously.md)),就可以在不影响其他组件正确性的情况下对这些组件进行优化。信息隐藏增加了软件的重用性,因为没有紧密耦合的组件通常在其他上下文中被证明是有用的,除了开发它们的上下文。最后,信息隐藏降低了构建大型系统的风险,因为即使系统没有成功,单个组件也可能被证明是成功的。 +由于许多原因,信息隐藏是重要的,其中大部分原因源于这样一个事实:它解耦了组成系统的组件,允许它们被单独开发、测试、优化、使用、理解和修改。这加速了系统开发,因为组件可以并行开发。它减轻了维护的负担,因为组件可以被更快地理解、调试或替换,而不必担心会损害其他组件。虽然信息隐藏本身不会导致良好的性能,但它能够进行有效的性能调优:一旦系统完成,概要分析确定了哪些组件会导致性能问题([Item-67](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9/Chapter-9-Item-67-Optimize-judiciously.md)),就可以在不影响其他组件正确性的情况下对这些组件进行优化。信息隐藏增加了软件的重用性,因为没有紧密耦合的组件通常在其他上下文中被证明是有用的,除了开发它们的上下文。最后,信息隐藏降低了构建大型系统的风险,因为即使系统没有成功,单个组件也可能被证明是成功的。 Java has many facilities to aid in information hiding. The access control mechanism [JLS, 6.6] specifies the accessibility of classes, interfaces, and members. The accessibility of an entity is determined by the location of its declaration and by which, if any, of the access modifiers (private,protected, and public) is present on the declaration. Proper use of these modifiers is essential to information hiding. @@ -24,7 +24,7 @@ For top-level (non-nested) classes and interfaces, there are only two possible a If a package-private top-level class or interface is used by only one class,consider making the top-level class a private static nested class of the sole class that uses it (Item 24). This reduces its accessibility from all the classes in its package to the one class that uses it. But it is far more important to reduce the accessibility of a gratuitously public class than of a package-private top-level class: the public class is part of the package’s API, while the package-private top-level class is already part of its implementation. -如果包私有顶级类或接口只被一个类使用,那么考虑将顶级类设置为使用它的唯一类的私有静态嵌套类([Item-24](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-24-Favor-static-member-classes-over-nonstatic.md))。这降低了它从包中的所有类到使用它的类的可访问性。但是,减少免费公共类的可访问性比减少包-私有顶级类的可访问性重要得多:公共类是包的API的一部分,而包-私有顶级类已经是包的实现的一部分。 +如果包私有顶级类或接口只被一个类使用,那么考虑将顶级类设置为使用它的唯一类的私有静态嵌套类([Item-24](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-24-Favor-static-member-classes-over-nonstatic.md))。这降低了它从包中的所有类到使用它的类的可访问性。但是,减少免费公共类的可访问性比减少包-私有顶级类的可访问性重要得多:公共类是包的API的一部分,而包-私有顶级类已经是包的实现的一部分。 For members (fields, methods, nested classes, and nested interfaces), there are four possible access levels, listed here in order of increasing accessibility: @@ -48,15 +48,15 @@ For members (fields, methods, nested classes, and nested interfaces), there are After carefully designing your class’s public API, your reflex(n. 反射;反映;映像;回复;习惯性思维 adj. 反射的;反省的;反作用的) should be to make all other members private. Only if another class in the same package really needs to access a member should you remove the private modifier, making the member package-private. If you find yourself doing this often, you should reexamine(重新审视) the design of your system to see if another decomposition(n. 分解,腐烂;变质) might yield classes that are better decoupled from one another. That said, both private and package-private members are part of a class’s implementation and do not normally impact its exported API. These fields can, however, “leak” into the exported API if the class implements Serializable (Items 86 and 87). -在仔细设计了类的公共API之后,您的反射应该是使所有其他成员都是私有的。只有当同一包中的另一个类确实需要访问一个成员时,您才应该删除私有修饰符,使成员包成为私有的。如果您发现自己经常这样做,那么您应该重新检查系统的设计,看看另一个分解是否会产生更好地相互分离的类。也就是说,私有成员和包私有成员都是类实现的一部分,通常不会影响其导出的API。但是,如果类实现了Serializable([Item-86](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-12-Item-86-Implement-Serializable-with-great-caution.md)和[Item-87](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-12-Item-87-Consider-using-a-custom-serialized-form.md)),这些字段可能会“泄漏”到导出的API中。 +在仔细设计了类的公共API之后,您的反射应该是使所有其他成员都是私有的。只有当同一包中的另一个类确实需要访问一个成员时,您才应该删除私有修饰符,使成员包成为私有的。如果您发现自己经常这样做,那么您应该重新检查系统的设计,看看另一个分解是否会产生更好地相互分离的类。也就是说,私有成员和包私有成员都是类实现的一部分,通常不会影响其导出的API。但是,如果类实现了Serializable([Item-86](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-12/Chapter-12-Item-86-Implement-Serializable-with-great-caution.md)和[Item-87](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-12/Chapter-12-Item-87-Consider-using-a-custom-serialized-form.md)),这些字段可能会“泄漏”到导出的API中。 For members of public classes, a huge increase in accessibility occurs when the access level goes from package-private to protected. A protected member is part of the class’s exported API and must be supported forever. Also, a protected member of an exported class represents a public commitment to an implementation detail (Item 19). The need for protected members should be relatively rare. -对于公共类的成员来说,当访问级别从包私有到受保护时,可访问性会有很大的提高。受保护的成员是类导出API的一部分,必须永远支持。此外,导出类的受保护成员表示对实现细节的公开承诺([Item-19](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-19-Design-and-document-for-inheritance-or-else-prohibit-it.md))。对受保护成员的需求应该相对较少。 +对于公共类的成员来说,当访问级别从包私有到受保护时,可访问性会有很大的提高。受保护的成员是类导出API的一部分,必须永远支持。此外,导出类的受保护成员表示对实现细节的公开承诺([Item-19](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-19-Design-and-document-for-inheritance-or-else-prohibit-it.md))。对受保护成员的需求应该相对较少。 There is a key rule that restricts your ability to reduce the accessibility of methods. If a method overrides a superclass method, it cannot have a more restrictive access level in the subclass than in the superclass [JLS, 8.4.8.3]. This is necessary to ensure that an instance of the subclass is usable anywhere that an instance of the superclass is usable (the Liskov substitution principle, see Item15). If you violate this rule, the compiler will generate an error message when you try to compile the subclass. A special case of this rule is that if a class implements an interface, all of the class methods that are in the interface must be declared public in the class. -有一个关键规则限制了您减少方法可访问性的能力。如果一个方法覆盖了超类方法,那么它在子类中的访问级别就不能比在超类[JLS, 8.4.8.3]中更严格。这对于确保子类的实例在超类的实例可用的任何地方都可用是必要的(Liskov替换原则,请参阅[Item-15](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-15-Minimize-the-accessibility-of-classes-and-members.md))。如果违反此规则,编译器将在尝试编译子类时生成错误消息。这个规则的一个特殊情况是,如果一个类实现了一个接口,那么该接口中的所有类方法都必须在类中声明为public。 +有一个关键规则限制了您减少方法可访问性的能力。如果一个方法覆盖了超类方法,那么它在子类中的访问级别就不能比在超类[JLS, 8.4.8.3]中更严格。这对于确保子类的实例在超类的实例可用的任何地方都可用是必要的(Liskov替换原则,请参阅[Item-15](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-15-Minimize-the-accessibility-of-classes-and-members.md))。如果违反此规则,编译器将在尝试编译子类时生成错误消息。这个规则的一个特殊情况是,如果一个类实现了一个接口,那么该接口中的所有类方法都必须在类中声明为public。 To facilitate(vt. 促进;帮助;使容易) testing your code, you may be tempted to make a class, interface,or member more accessible than otherwise necessary. This is fine up to a point.It is acceptable to make a private member of a public class package-private in order to test it, but it is not acceptable to raise the accessibility any higher. In other words, it is not acceptable to make a class, interface, or member a part of a pack-age’s exported API to facilitate testing. Luckily, it isn’t necessary either because tests can be made to run as part of the package being tested, thus gaining access to its package-private elements. @@ -64,11 +64,11 @@ To facilitate(vt. 促进;帮助;使容易) testing your code, you may be **Instance fields of public classes should rarely be public** (Item 16). If an instance field is nonfinal or is a reference to a mutable object, then by making it public, you give up the ability to limit the values that can be stored in the field.This means you give up the ability to enforce invariants involving the field.Also, you give up the ability to take any action when the field is modified, so **classes with public mutable fields are not generally thread-safe.** Even if a field is final and refers to an immutable object, by making it public you give up the flexibility to switch to a new internal data representation in which the field does not exist. -**公共类的实例字段很少是公共的**([Item-16](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-16-In-public-classes-use-accessor-methods-not-public-fields.md))。如果实例字段是非final的,或者是对可变对象的引用,那么通过将其公开,您就放弃了限制字段中可以存储的值的能力。这意味着您放弃了强制包含字段的不变量的能力。此外,您还放弃了在修改字段时采取任何操作的能力,因此带有公共可变字段的 **类通常不是线程安全的。** 即使一个字段是final的,并且引用了一个不可变的对象,通过将其公开,您放弃了切换到一个新的内部数据表示的灵活性,而该字段并不存在。 +**公共类的实例字段很少是公共的**([Item-16](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-16-In-public-classes-use-accessor-methods-not-public-fields.md))。如果实例字段是非final的,或者是对可变对象的引用,那么通过将其公开,您就放弃了限制字段中可以存储的值的能力。这意味着您放弃了强制包含字段的不变量的能力。此外,您还放弃了在修改字段时采取任何操作的能力,因此带有公共可变字段的 **类通常不是线程安全的。** 即使一个字段是final的,并且引用了一个不可变的对象,通过将其公开,您放弃了切换到一个新的内部数据表示的灵活性,而该字段并不存在。 The same advice applies to static fields, with one exception. You can expose constants via public static final fields, assuming the constants form an integral part of the abstraction provided by the class. By convention, such fields have names consisting of capital letters, with words separated by underscores (Item 68). It is critical that these fields contain either primitive values or references to immutable objects (Item 17). a field containing a reference to a mutable object has all the disadvantages of a nonfinal field. While the reference cannot be modified, the referenced object can be modified—with disastrous results. -同样的建议也适用于静态字段,只有一个例外。您可以通过公共静态final字段公开常量,假设这些常量是类提供的抽象的组成部分。按照惯例,这些字段的名称由大写字母组成,单词以下划线分隔([Item-68](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9-Item-68-Adhere-to-generally-accepted-naming-conventions.md))。重要的是,这些字段要么包含原始值,要么包含对不可变对象的引用([Item-17](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-17-Minimize-mutability.md))。包含对可变对象的引用的字段具有非final字段的所有缺点。虽然引用不能被修改,但是引用的对象可以被修改——这会导致灾难性的后果。 +同样的建议也适用于静态字段,只有一个例外。您可以通过公共静态final字段公开常量,假设这些常量是类提供的抽象的组成部分。按照惯例,这些字段的名称由大写字母组成,单词以下划线分隔([Item-68](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9/Chapter-9-Item-68-Adhere-to-generally-accepted-naming-conventions.md))。重要的是,这些字段要么包含原始值,要么包含对不可变对象的引用([Item-17](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-17-Minimize-mutability.md))。包含对可变对象的引用的字段具有非final字段的所有缺点。虽然引用不能被修改,但是引用的对象可以被修改——这会导致灾难性的后果。 Note that a nonzero-length array is always mutable, so it is wrong for a class to have a public static final array field, or an accessor that returns such a field. If a class has such a field or accessor, clients will be able to modify the contents of the array. This is a frequent source of security holes: diff --git a/Chapter-4-Item-16-In-public-classes-use-accessor-methods-not-public-fields.md b/Chapter-4/Chapter-4-Item-16-In-public-classes-use-accessor-methods-not-public-fields.md similarity index 92% rename from Chapter-4-Item-16-In-public-classes-use-accessor-methods-not-public-fields.md rename to Chapter-4/Chapter-4-Item-16-In-public-classes-use-accessor-methods-not-public-fields.md index 76413df..4c4f752 100644 --- a/Chapter-4-Item-16-In-public-classes-use-accessor-methods-not-public-fields.md +++ b/Chapter-4/Chapter-4-Item-16-In-public-classes-use-accessor-methods-not-public-fields.md @@ -16,7 +16,7 @@ class Point { Because the data fields of such classes are accessed directly, these classes do not offer the benefits of encapsulation(n. 封装;包装) (Item 15). You can’t change the representation(n. 代表;表现;表示法;陈述) without changing the API, you can’t enforce invariants, and you can’t take auxiliary(adj. 辅助的;副的;附加的) action when a field is accessed. Hard-line object-oriented programmers feel that such classes are anathema(n. 诅咒;革出教门;被诅咒者;令人厌恶的人) and should always be replaced by classes with private fields and public accessor methods (getters) and, for mutable classes, mutators (setters): -因为这些类的数据字段是直接访问的,所以这些类没有提供封装的好处([Item-15](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-15-Minimize-the-accessibility-of-classes-and-members.md))。不改变API就不能改变表示,不能强制执行不变量,也不能在访问字段时采取辅助操作。强硬的面向对象程序员认为这样的类是令人厌恶的,应该总是被私有字段和公共访问方法(getter)的类所取代,对于可变类,则是mutators (setter): +因为这些类的数据字段是直接访问的,所以这些类没有提供封装的好处([Item-15](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-15-Minimize-the-accessibility-of-classes-and-members.md))。不改变API就不能改变表示,不能强制执行不变量,也不能在访问字段时采取辅助操作。强硬的面向对象程序员认为这样的类是令人厌恶的,应该总是被私有字段和公共访问方法(getter)的类所取代,对于可变类,则是mutators (setter): ``` // Encapsulation of data by accessor methods and mutators @@ -44,7 +44,7 @@ However, if a class is package-private or is a private nested class, there is no Several classes in the Java platform libraries violate the advice that public classes should not expose fields directly. Prominent examples include the Point and Dimension classes in the java.awt package. Rather than examples to be emulated, these classes should be regarded as cautionary tales.As described in Item 67, the decision to expose the internals of the Dimension class resulted in a serious performance problem that is still with us today. -Java平台库中的几个类违反了公共类不应该直接公开字段的建议。突出的例子包括java.awt包中的Point和维度类。这些课程不应被效仿,而应被视为警示故事。正如[Item-67](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9-Item-67-Optimize-judiciously.md)所述,决定公开维度类的内部结构导致了严重的性能问题,这种问题至今仍存在。 +Java平台库中的几个类违反了公共类不应该直接公开字段的建议。突出的例子包括java.awt包中的Point和维度类。这些课程不应被效仿,而应被视为警示故事。正如[Item-67](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9/Chapter-9-Item-67-Optimize-judiciously.md)所述,决定公开维度类的内部结构导致了严重的性能问题,这种问题至今仍存在。 While it’s never a good idea for a public class to expose fields directly, it is less harmful if the fields are immutable. You can’t change the representation of such a class without changing its API, and you can’t take auxiliary actions when a field is read, but you can enforce invariants. For example, this class guarantees that each instance represents a valid time: diff --git a/Chapter-4-Item-17-Minimize-mutability.md b/Chapter-4/Chapter-4-Item-17-Minimize-mutability.md similarity index 90% rename from Chapter-4-Item-17-Minimize-mutability.md rename to Chapter-4/Chapter-4-Item-17-Minimize-mutability.md index be3a055..fe2001a 100644 --- a/Chapter-4-Item-17-Minimize-mutability.md +++ b/Chapter-4/Chapter-4-Item-17-Minimize-mutability.md @@ -24,15 +24,15 @@ To make a class immutable, follow these five rules: 4. **Make all fields private.** This prevents clients from obtaining access to mutable objects referred to by fields and modifying these objects directly.While it is technically permissible for immutable classes to have public final fields containing primitive values or references to immutable objects, it is not recommended because it precludes changing the internal representation in a later release (Items 15 and 16). -**使所有字段为私有。** 这将阻止客户端访问字段引用的可变对象并直接修改这些对象。虽然在技术上允许不可变类拥有包含基元值或对不可变对象的引用的公共final字段,但不建议这样做,因为它排除了在以后的版本中更改内部表示([Item-15](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-15-Minimize-the-accessibility-of-classes-and-members.md)和[Item-16](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-16-In-public-classes-use-accessor-methods-not-public-fields.md))。 +**使所有字段为私有。** 这将阻止客户端访问字段引用的可变对象并直接修改这些对象。虽然在技术上允许不可变类拥有包含基元值或对不可变对象的引用的公共final字段,但不建议这样做,因为它排除了在以后的版本中更改内部表示([Item-15](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-15-Minimize-the-accessibility-of-classes-and-members.md)和[Item-16](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-16-In-public-classes-use-accessor-methods-not-public-fields.md))。 5. **Ensure exclusive access to any mutable components.** If your class has any fields that refer to mutable objects, ensure that clients of the class cannot obtain references to these objects. Never initialize such a field to a clientprovided object reference or return the field from an accessor. Make defensive copies (Item 50) in constructors, accessors, and readObject methods (Item 88). -**确保对任何可变组件的独占访问。** 如果您的类有任何引用可变对象的字段,请确保该类的客户端无法获得对这些对象的引用。永远不要将这样的字段初始化为clientprovide对象引用或从访问器返回字段。在构造函数、访问器和readObject方法([Item-88](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-12-Item-88-Write-readObject-methods-defensively.md))中创建防御性副本([Item-50](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-8-Item-50-Make-defensive-copies-when-needed.md))。 +**确保对任何可变组件的独占访问。** 如果您的类有任何引用可变对象的字段,请确保该类的客户端无法获得对这些对象的引用。永远不要将这样的字段初始化为clientprovide对象引用或从访问器返回字段。在构造函数、访问器和readObject方法([Item-88](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-12/Chapter-12-Item-88-Write-readObject-methods-defensively.md))中创建防御性副本([Item-50](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-8/Chapter-8-Item-50-Make-defensive-copies-when-needed.md))。 Many of the example classes in previous items are immutable. One such class is PhoneNumber in Item 11, which has accessors for each attribute but no corresponding mutators. Here is a slightly more complex example: -前面项目中的许多示例类都是不可变的。其中一个类是[Item-11](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3-Item-11-Always-override-hashCode-when-you-override-equals.md)中的PhoneNumber,它对每个属性都有访问器,但没有相应的mutators。下面是一个稍微复杂一点的例子: +前面项目中的许多示例类都是不可变的。其中一个类是[Item-11](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3/Chapter-3-Item-11-Always-override-hashCode-when-you-override-equals.md)中的PhoneNumber,它对每个属性都有访问器,但没有相应的mutators。下面是一个稍微复杂一点的例子: ``` // Immutable complex number class @@ -102,11 +102,11 @@ public static final Complex I = new Complex(0, 1); This approach can be taken one step further. An immutable class can provide static factories (Item 1) that cache frequently requested instances to avoid creating new instances when existing ones would do. All the boxed primitive classes and BigInteger do this. Using such static factories causes clients to share instances instead of creating new ones, reducing memory footprint and garbage collection costs. Opting for static factories in place of public constructors when designing a new class gives you the flexibility to add caching later, without modifying clients. -这种方法可以更进一步。不可变类可以提供静态工厂([Item-1](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2-Item-1-Consider-static-factory-methods-instead-of-constructors.md)),这些工厂缓存经常请求的实例,以避免在现有实例可用时创建新实例。所有包装类和BigInteger都是这样做的。使用这种静态工厂会导致客户机共享实例而不是创建新实例,从而减少内存占用和垃圾收集成本。在设计新类时,选择静态工厂而不是公共构造函数,这使您能够灵活地在以后添加缓存,而无需修改客户机。 +这种方法可以更进一步。不可变类可以提供静态工厂([Item-1](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2/Chapter-2-Item-1-Consider-static-factory-methods-instead-of-constructors.md)),这些工厂缓存经常请求的实例,以避免在现有实例可用时创建新实例。所有包装类和BigInteger都是这样做的。使用这种静态工厂会导致客户机共享实例而不是创建新实例,从而减少内存占用和垃圾收集成本。在设计新类时,选择静态工厂而不是公共构造函数,这使您能够灵活地在以后添加缓存,而无需修改客户机。 A consequence(n. 结果;重要性;推论) of the fact that immutable objects can be shared freely is that you never have to make defensive copies of them (Item 50). In fact, you never have to make any copies at all because the copies would be forever equivalent to the originals. Therefore, you need not and should not provide a clone method or copy constructor (Item 13) on an immutable class. This was not well understood in the early days of the Java platform, so the String class does have a copy constructor, but it should rarely, if ever, be used (Item 6). -不可变对象可以自由共享这一事实的一个后果是,您永远不需要对它们进行防御性的复制([Item-50](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-8-Item-50-Make-defensive-copies-when-needed.md))。事实上,你根本不需要做任何拷贝,因为拷贝将永远等同于原件。因此,您不需要也不应该在不可变类上提供克隆方法或复制构造函数([Item-13](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3-Item-13-Override-clone-judiciously.md))。这在Java平台的早期并没有得到很好的理解,因此String类确实有一个复制构造函数,但是如果有的话,应该很少使用它([Item-6](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2-Item-6-Avoid-creating-unnecessary-objects.md))。 +不可变对象可以自由共享这一事实的一个后果是,您永远不需要对它们进行防御性的复制([Item-50](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-8/Chapter-8-Item-50-Make-defensive-copies-when-needed.md))。事实上,你根本不需要做任何拷贝,因为拷贝将永远等同于原件。因此,您不需要也不应该在不可变类上提供克隆方法或复制构造函数([Item-13](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3/Chapter-3-Item-13-Override-clone-judiciously.md))。这在Java平台的早期并没有得到很好的理解,因此String类确实有一个复制构造函数,但是如果有的话,应该很少使用它([Item-6](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2/Chapter-2-Item-6-Avoid-creating-unnecessary-objects.md))。 **Not only can you share immutable objects, but they can share their internals.** For example, the BigInteger class uses a sign-magnitude representation internally. The sign is represented by an int, and the magnitude is represented by an int array. The negate method produces a new BigInteger of like magnitude and opposite sign. It does not need to copy the array even though it is mutable; the newly created BigInteger points to the same internal array as the original. @@ -118,7 +118,7 @@ A consequence(n. 结果;重要性;推论) of the fact that immutable obj **Immutable objects provide failure atomicity for free** (Item 76). Their state never changes, so there is no possibility of a temporary inconsistency. -不可变对象免费提供故障原子性([Item-76](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-10-Item-76-Strive-for-failure-atomicity.md))。他们的状态从未改变,所以不可能出现暂时的不一致。 +不可变对象免费提供故障原子性([Item-76](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-10/Chapter-10-Item-76-Strive-for-failure-atomicity.md))。他们的状态从未改变,所以不可能出现暂时的不一致。 **The major disadvantage of immutable classes is that they require a separate object for each distinct value.** Creating these objects can be costly,especially if they are large. For example, suppose that you have a million-bit BigInteger and you want to change its low-order bit: @@ -148,7 +148,7 @@ The package-private mutable companion class approach works fine if you can accur Now that you know how to make an immutable class and you understand the pros and cons of immutability, let’s discuss a few design alternatives. Recall that to guarantee immutability, a class must not permit itself to be subclassed. This can be done by making the class final, but there is another, more flexible alternative. Instead of making an immutable class final, you can make all of its constructors private or package-private and add public static factories in place of the public constructors (Item 1). To make this concrete, here’s how Complex would look if you took this approach: -既然您已经知道了如何创建不可变类,并且了解了不可变性的优缺点,那么让我们来讨论一些设计方案。回想一下,为了保证不变性,类不允许自己被子类化。这可以通过期末考试来完成,但是还有另外一个更灵活的选择。与使不可变类成为final不同,您可以将其所有构造函数变为私有或包-私有,并在公共构造函数的位置添加公共静态工厂([Item-1](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2-Item-1-Consider-static-factory-methods-instead-of-constructors.md))。 +既然您已经知道了如何创建不可变类,并且了解了不可变性的优缺点,那么让我们来讨论一些设计方案。回想一下,为了保证不变性,类不允许自己被子类化。这可以通过期末考试来完成,但是还有另外一个更灵活的选择。与使不可变类成为final不同,您可以将其所有构造函数变为私有或包-私有,并在公共构造函数的位置添加公共静态工厂([Item-1](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2/Chapter-2-Item-1-Consider-static-factory-methods-instead-of-constructors.md))。 ``` // Immutable class with static factories instead of constructors @@ -172,7 +172,7 @@ This approach is often the best alternative. It is the most flexible because it It was not widely understood that immutable classes had to be effectively final when BigInteger and BigDecimal were written, so all of their methods may be overridden. Unfortunately, this could not be corrected after the fact while preserving backward compatibility. If you write a class whose security depends on the immutability of a BigInteger or BigDecimal argument from an untrusted client, you must check to see that the argument is a “real” BigInteger or BigDecimal, rather than an instance of an untrusted subclass. If it is the latter, you must defensively copy it under the assumption that it might be mutable (Item 50): -当编写BigInteger和BigDecimal时,不可变类必须是有效的final,因此可以重写它们的所有方法,这一点没有得到广泛的理解。遗憾的是,在保留向后兼容性的情况下,这一问题无法在事后得到纠正。如果您编写的类的安全性依赖于来自不受信任客户端的BigInteger或BigDecimal参数的不可变性,那么您必须检查该参数是否是“真正的”BigInteger或BigDecimal,而不是不受信任子类的实例。如果是后者,你必须防御地复制它,假设它可能是可变的([Item-50](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-8-Item-50-Make-defensive-copies-when-needed.md)): +当编写BigInteger和BigDecimal时,不可变类必须是有效的final,因此可以重写它们的所有方法,这一点没有得到广泛的理解。遗憾的是,在保留向后兼容性的情况下,这一问题无法在事后得到纠正。如果您编写的类的安全性依赖于来自不受信任客户端的BigInteger或BigDecimal参数的不可变性,那么您必须检查该参数是否是“真正的”BigInteger或BigDecimal,而不是不受信任子类的实例。如果是后者,你必须防御地复制它,假设它可能是可变的([Item-50](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-8/Chapter-8-Item-50-Make-defensive-copies-when-needed.md)): ``` public static BigInteger safeInstance(BigInteger val) { @@ -187,15 +187,15 @@ The list of rules for immutable classes at the beginning of this item says that For example, PhoneNumber’s hashCode method (Item 11, page 53) computes the hash code the first time it’s invoked and caches it in case it’s invoked again. This technique, an example of lazy initialization (Item 83), is also used by String. -例如,PhoneNumber的hashCode方法([Item-11](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3-Item-11-Always-override-hashCode-when-you-override-equals.md),第53页)在第一次调用时计算哈希代码,并缓存它,以防再次调用它。这个技术是一个延迟初始化的例子([Item-83](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-11-Item-83-Use-lazy-initialization-judiciously.md)),String也使用这个技术。 +例如,PhoneNumber的hashCode方法([Item-11](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3/Chapter-3-Item-11-Always-override-hashCode-when-you-override-equals.md),第53页)在第一次调用时计算哈希代码,并缓存它,以防再次调用它。这个技术是一个延迟初始化的例子([Item-83](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-11/Chapter-11-Item-83-Use-lazy-initialization-judiciously.md)),String也使用这个技术。 One caveat should be added concerning serializability. If you choose to have your immutable class implement Serializable and it contains one or more fields that refer to mutable objects, you must provide an explicit readObject or readResolve method, or use the ObjectOutputStream.writeUnshared and ObjectInputStream.readUnshared methods, even if the default serialized form is acceptable. Otherwise an attacker could create a mutable instance of your class. This topic is covered in detail in Item 88. -关于可序列化性,应该添加一个注意事项。如果您选择让不可变类实现Serializable,并且它包含一个或多个引用可变对象的字段,那么您必须提供一个显式的readObject或readResolve方法,或者使用ObjectOutputStream。writeUnshared ObjectInputStream。readUnshared方法,即使默认的序列化形式是可以接受的。否则攻击者可能创建类的可变实例。[Item-88](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-12-Item-88-Write-readObject-methods-defensively.md)详细讨论了这个主题。 +关于可序列化性,应该添加一个注意事项。如果您选择让不可变类实现Serializable,并且它包含一个或多个引用可变对象的字段,那么您必须提供一个显式的readObject或readResolve方法,或者使用ObjectOutputStream。writeUnshared ObjectInputStream。readUnshared方法,即使默认的序列化形式是可以接受的。否则攻击者可能创建类的可变实例。[Item-88](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-12/Chapter-12-Item-88-Write-readObject-methods-defensively.md)详细讨论了这个主题。 To summarize, resist the urge to write a setter for every getter. **Classes should be immutable unless there’s a very good reason to make them mutable.** Immutable classes provide many advantages, and their only disadvantage is the potential for performance problems under certain circumstances. You should always make small value objects, such as PhoneNumber and Complex, immutable. (There are several classes in the Java platform libraries, such as java.util.Date and java.awt.Point, that should have been immutable but aren’t.) You should seriously consider making larger value objects, such as String and BigInteger, immutable as well. You should provide a public mutable companion class for your immutable class only once you’ve confirmed that it’s necessary to achieve satisfactory performance (Item 67). -总结一下,抵制为每个getter编写setter的冲动。类应该是不可变的,除非有很好的理由让它们可变。不可变类提供了许多优点,它们唯一的缺点是在某些情况下可能出现性能问题。您应该始终创建小的值对象,例如PhoneNumber和Complex、stable。(Java平台库中有几个类,比如java.util.Date和java.awt.Point,这本来是不可改变的,但事实并非如此。您应该认真考虑将较大的值对象(如String和BigInteger)设置为不可变的。只有在确认了实现满意性能的必要性之后,才应该为不可变类提供一个公共可变伴侣类([Item-67](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9-Item-67-Optimize-judiciously.md))。 +总结一下,抵制为每个getter编写setter的冲动。类应该是不可变的,除非有很好的理由让它们可变。不可变类提供了许多优点,它们唯一的缺点是在某些情况下可能出现性能问题。您应该始终创建小的值对象,例如PhoneNumber和Complex、stable。(Java平台库中有几个类,比如java.util.Date和java.awt.Point,这本来是不可改变的,但事实并非如此。您应该认真考虑将较大的值对象(如String和BigInteger)设置为不可变的。只有在确认了实现满意性能的必要性之后,才应该为不可变类提供一个公共可变伴侣类([Item-67](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9/Chapter-9-Item-67-Optimize-judiciously.md))。 There are some classes for which immutability is impractical. **If a class cannot be made immutable, limit its mutability as much as possible.** Reducing the number of states in which an object can exist makes it easier to reason about the object and reduces the likelihood of errors. Therefore, make every field final unless there is a compelling reason to make it nonfinal. Combining the advice of this item with that of Item 15, your natural inclination should be to **declare every field private final unless there’s a good reason to do otherwise.** diff --git a/Chapter-4-Item-18-Favor-composition-over-inheritance.md b/Chapter-4/Chapter-4-Item-18-Favor-composition-over-inheritance.md similarity index 98% rename from Chapter-4-Item-18-Favor-composition-over-inheritance.md rename to Chapter-4/Chapter-4-Item-18-Favor-composition-over-inheritance.md index ae10144..844e1a0 100644 --- a/Chapter-4-Item-18-Favor-composition-over-inheritance.md +++ b/Chapter-4/Chapter-4-Item-18-Favor-composition-over-inheritance.md @@ -4,7 +4,7 @@ Inheritance is a powerful way to achieve(vt. 取得;获得;实现;成功) code reuse(n. 重新使用,再用), but it is not always the best tool for the job. Used inappropriately(adv. 不适当地), it leads to fragile(adj. 脆的;易碎的) software. It is safe to use inheritance within a package, where the subclass and the superclass implementations are under the control of the same programmers. It is also safe to use inheritance when extending classes specifically designed and documented for extension (Item 19). Inheriting from ordinary concrete classes across package boundaries, however, is dangerous. As a reminder, this book uses the word “inheritance” to mean implementation inheritance (when one class extends another). The problems discussed in this item do not apply to interface inheritance (when a class implements an interface or when one interface extends another). -继承是实现代码重用的一种强大方法,但它并不总是最佳的工具。使用不当会导致软件脆弱。在包中使用继承是安全的,其中子类和超类实现由相同的程序员控制。在扩展专门为扩展设计和文档化的类时使用继承也是安全的([Item-19](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-19-Design-and-document-for-inheritance-or-else-prohibit-it.md))。然而,对普通的具体类进行跨包边界的继承是危险的。作为提醒,本书使用“继承”一词来表示实现继承(当一个类扩展另一个类时)。本项目中讨论的问题不适用于接口继承(当类实现接口或一个接口扩展另一个接口时)。 +继承是实现代码重用的一种强大方法,但它并不总是最佳的工具。使用不当会导致软件脆弱。在包中使用继承是安全的,其中子类和超类实现由相同的程序员控制。在扩展专门为扩展设计和文档化的类时使用继承也是安全的([Item-19](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-19-Design-and-document-for-inheritance-or-else-prohibit-it.md))。然而,对普通的具体类进行跨包边界的继承是危险的。作为提醒,本书使用“继承”一词来表示实现继承(当一个类扩展另一个类时)。本项目中讨论的问题不适用于接口继承(当类实现接口或一个接口扩展另一个接口时)。 Unlike method invocation, inheritance violates encapsulation [Snyder86].In other words, a subclass depends on the implementation details of its superclass for its proper function. The superclass’s implementation may change from release to release, and if it does, the subclass may break, even though its code has not been touched. As a consequence, a subclass must evolve in tandem with its superclass, unless the superclass’s authors have designed and documented it specifically for the purpose of being extended. diff --git a/Chapter-4-Item-19-Design-and-document-for-inheritance-or-else-prohibit-it.md b/Chapter-4/Chapter-4-Item-19-Design-and-document-for-inheritance-or-else-prohibit-it.md similarity index 97% rename from Chapter-4-Item-19-Design-and-document-for-inheritance-or-else-prohibit-it.md rename to Chapter-4/Chapter-4-Item-19-Design-and-document-for-inheritance-or-else-prohibit-it.md index 10ccda3..b2a818e 100644 --- a/Chapter-4-Item-19-Design-and-document-for-inheritance-or-else-prohibit-it.md +++ b/Chapter-4/Chapter-4-Item-19-Design-and-document-for-inheritance-or-else-prohibit-it.md @@ -2,7 +2,7 @@ ### Item 19: Design and document for inheritance or else prohibit it(继承要设计良好并且具有文档,否则禁止使用) -[Item-18](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-18-Favor-composition-over-inheritance.md) alerted you to the dangers of subclassing a “foreign” class that was not designed and documented for inheritance. So what does it mean for a class to be designed and documented for inheritance? +[Item-18](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-18-Favor-composition-over-inheritance.md) alerted you to the dangers of subclassing a “foreign” class that was not designed and documented for inheritance. So what does it mean for a class to be designed and documented for inheritance? 第18项提醒您注意子类化不是为继承设计和文档化的“外部”类的危险。那么,为继承而设计和文档化的类意味着什么呢? @@ -146,7 +146,7 @@ Finally, if you decide to implement Serializable in a class designed for inherit By now it should be apparent that designing a class for inheritance requires great effort and places substantial limitations on the class. This is not a decision to be undertaken lightly. There are some situations where it is clearly the right thing to do, such as abstract classes, including skeletal implementations of interfaces (Item 20). There are other situations where it is clearly the wrong thing to do, such as immutable classes (Item 17). -到目前为止,显然为继承而设计一个类需要付出很大的努力,并且对类有很大的限制。这不是一个可以轻易作出的决定。在某些情况下,这样做显然是正确的,例如抽象类,包括接口的骨架实现(项目20)。还有一些情况显然是错误的,比如不可变类([Item-17](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-17-Minimize-mutability.md))。 +到目前为止,显然为继承而设计一个类需要付出很大的努力,并且对类有很大的限制。这不是一个可以轻易作出的决定。在某些情况下,这样做显然是正确的,例如抽象类,包括接口的骨架实现(项目20)。还有一些情况显然是错误的,比如不可变类([Item-17](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-17-Minimize-mutability.md))。 But what about ordinary concrete classes? Traditionally, they are neither final nor designed and documented for subclassing, but this state of affairs is dangerous. Each time a change is made in such a class, there is a chance that subclasses extending the class will break. This is not just a theoretical problem.It is not uncommon to receive subclassing-related bug reports after modifying the internals of a nonfinal concrete class that was not designed and documented for inheritance. @@ -154,11 +154,11 @@ But what about ordinary concrete classes? Traditionally, they are neither final The best solution to this problem is to prohibit subclassing in classes that are not designed and documented to be safely subclassed. There are two ways to prohibit subclassing. The easier of the two is to declare the class final. The alternative is to make all the constructors private or package-private and to add public static factories in place of the constructors. This alternative, which provides the flexibility to use subclasses internally, is discussed in Item 17. Either approach is acceptable. -这个问题的最佳解决方案是禁止在没有设计和文档记录的类中进行子类化。有两种方法可以禁止子类化。两者中比较容易的是声明类final。另一种方法是将所有构造函数变为私有或包私有,并在构造函数的位置添加公共静态工厂。这个替代方案提供了内部使用子类的灵活性,在[Item-17](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-17-Minimize-mutability.md)中进行了讨论。两种方法都可以接受。 +这个问题的最佳解决方案是禁止在没有设计和文档记录的类中进行子类化。有两种方法可以禁止子类化。两者中比较容易的是声明类final。另一种方法是将所有构造函数变为私有或包私有,并在构造函数的位置添加公共静态工厂。这个替代方案提供了内部使用子类的灵活性,在[Item-17](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-17-Minimize-mutability.md)中进行了讨论。两种方法都可以接受。 This advice may be somewhat controversial because many programmers have grown accustomed to subclassing ordinary concrete classes to add facilities such as instrumentation, notification, and synchronization or to limit functionality. If a class implements some interface that captures its essence, such as Set, List, or Map, then you should feel no compunction about prohibiting subclassing. The wrapper class pattern, described in Item 18, provides a superior alternative to inheritance for augmenting the functionality. -这个建议可能有点争议,因为许多程序员已经习惯了子类化普通的具体类,以添加工具、通知和同步等功能或限制功能。如果一个类实现了某个接口,该接口捕获了它的本质,例如Set、List或Map,那么您不应该对禁止子类化感到内疚。在[Item-18](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-18-Favor-composition-over-inheritance.md)中描述的包装器类模式提供了一种优于继承的方法来增强功能。 +这个建议可能有点争议,因为许多程序员已经习惯了子类化普通的具体类,以添加工具、通知和同步等功能或限制功能。如果一个类实现了某个接口,该接口捕获了它的本质,例如Set、List或Map,那么您不应该对禁止子类化感到内疚。在[Item-18](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-18-Favor-composition-over-inheritance.md)中描述的包装器类模式提供了一种优于继承的方法来增强功能。 If a concrete class does not implement a standard interface, then you may inconvenience some programmers by prohibiting inheritance. If you feel that you must allow inheritance from such a class, one reasonable approach is to ensure that the class never invokes any of its overridable methods and to document this fact. In other words, eliminate the class’s self-use of overridable methods entirely. In doing so, you’ll create a class that is reasonably safe to subclass. Overriding a method will never affect the behavior of any other method. diff --git a/Chapter-4-Item-20-Prefer-interfaces-to-abstract-classes.md b/Chapter-4/Chapter-4-Item-20-Prefer-interfaces-to-abstract-classes.md similarity index 94% rename from Chapter-4-Item-20-Prefer-interfaces-to-abstract-classes.md rename to Chapter-4/Chapter-4-Item-20-Prefer-interfaces-to-abstract-classes.md index dd5b00e..4a958d4 100644 --- a/Chapter-4-Item-20-Prefer-interfaces-to-abstract-classes.md +++ b/Chapter-4/Chapter-4-Item-20-Prefer-interfaces-to-abstract-classes.md @@ -51,11 +51,11 @@ You don’t always need this level of flexibility, but when you do, interfaces a Interfaces enable safe, powerful functionality(n. 功能;[数] 泛函性,函数性) enhancements via the wrapper class idiom (Item 18). If you use abstract classes to define types, you leave the programmer who wants to add functionality with no alternative but inheritance. The resulting classes are less powerful and more fragile than wrapper classes. -通过[Item-18](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-18-Favor-composition-over-inheritance.md)介绍的包装类,接口能够支持安全、强大的功能增强。如果您使用抽象类来定义类型,那么您将让希望添加功能的程序员除了继承之外别无选择。最终生成的类不如包装类强大,也更脆弱。 +通过[Item-18](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-18-Favor-composition-over-inheritance.md)介绍的包装类,接口能够支持安全、强大的功能增强。如果您使用抽象类来定义类型,那么您将让希望添加功能的程序员除了继承之外别无选择。最终生成的类不如包装类强大,也更脆弱。 When there is an obvious implementation of an interface method in terms of other interface methods, consider providing implementation assistance to programmers in the form of a default method. For an example of this technique, see the removeIf method on page 104. If you provide default methods, be sure to document them for inheritance using the @implSpec Javadoc tag (Item 19). -当接口方法的其他接口方法有明显的实现时,考虑以默认方法的形式为程序员提供实现帮助。有关此技术的示例,请参阅第104页的removeIf方法。如果您提供了默认方法,请使用 **@implSpec** 标签,并确保在文档中记录他们的继承关系([Item-19](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-19-Design-and-document-for-inheritance-or-else-prohibit-it.md))。 +当接口方法的其他接口方法有明显的实现时,考虑以默认方法的形式为程序员提供实现帮助。有关此技术的示例,请参阅第104页的removeIf方法。如果您提供了默认方法,请使用 **@implSpec** 标签,并确保在文档中记录他们的继承关系([Item-19](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-19-Design-and-document-for-inheritance-or-else-prohibit-it.md))。 There are limits on how much implementation assistance(n. 援助,帮助;辅助设备) you can provide with default methods. Although many interfaces specify the behavior of Object methods such as equals and hashCode, you are not permitted to provide default methods for them. Also, interfaces are not permitted to contain instance fields or nonpublic static members (with the exception of private static methods). Finally, you can’t add default methods to an interface that you don’t control. @@ -98,11 +98,11 @@ static List intArrayAsList(int[] a) { When you consider all that a List implementation does for you, this example is an impressive demonstration of the power of skeletal implementations. Incidentally, this example is an Adapter [Gamma95] that allows an int array to be viewed as a list of Integer instances. Because of all the translation back and forth between int values and Integer instances (boxing and unboxing), its performance is not terribly good. Note that the implementation takes the form of an anonymous class (Item 24). -当您考虑到List实现为您做的所有事情时,这个例子是骨架实现强大功能的一个令人印象深刻的演示。顺便说一句,这个示例是一个Adapter(适配器)[Gamma95],它允许将int数组视为Integer实例的list。因为在int值和Integer实例(装箱和拆箱)之间来回转换,所以它的性能不是很好。注意,实现的形式是匿名类([Item-24](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-24-Favor-static-member-classes-over-nonstatic.md))。 +当您考虑到List实现为您做的所有事情时,这个例子是骨架实现强大功能的一个令人印象深刻的演示。顺便说一句,这个示例是一个Adapter(适配器)[Gamma95],它允许将int数组视为Integer实例的list。因为在int值和Integer实例(装箱和拆箱)之间来回转换,所以它的性能不是很好。注意,实现的形式是匿名类([Item-24](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-24-Favor-static-member-classes-over-nonstatic.md))。 The beauty of skeletal implementation classes is that they provide all of the implementation assistance of abstract classes without imposing the severe constraints that abstract classes impose when they serve as type definitions. For most implementors of an interface with a skeletal implementation class, extending this class is the obvious choice, but it is strictly optional. If a class cannot be made to extend the skeletal implementation, the class can always implement the interface directly. The class still benefits from any default methods present on the interface itself. Furthermore, the skeletal implementation can still aid the implementor’s task. The class implementing the interface can forward invocations of interface methods to a contained instance of a private inner class that extends the skeletal implementation. This technique, known as simulated multiple inheritance, is closely related to the wrapper class idiom discussed in Item 18. It provides many of the benefits of multiple inheritance, while avoiding the pitfalls. -骨架实现类的美妙之处在于,它们提供了抽象类的所有实现帮助,而不像抽象类作为类型定义时那样受到严格的约束。对于具有骨架实现类的接口的大多数实现来说,扩展这个类是显而易见的选择,但它并不是必需的。如果不能使类扩展骨架实现,则类总是可以直接实现接口。类仍然受益于接口本身的任何默认方法。此外,骨架实现仍然可以帮助实现人员完成任务。实现接口的类可以将接口方法的调用转发给扩展骨架实现的私有内部类的包含实例。这种技术称为模拟多重继承,与[Item-18](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-18-Favor-composition-over-inheritance.md)中讨论的包装类密切相关。它提供了多重继承的许多好处,同时避免了缺陷。 +骨架实现类的美妙之处在于,它们提供了抽象类的所有实现帮助,而不像抽象类作为类型定义时那样受到严格的约束。对于具有骨架实现类的接口的大多数实现来说,扩展这个类是显而易见的选择,但它并不是必需的。如果不能使类扩展骨架实现,则类总是可以直接实现接口。类仍然受益于接口本身的任何默认方法。此外,骨架实现仍然可以帮助实现人员完成任务。实现接口的类可以将接口方法的调用转发给扩展骨架实现的私有内部类的包含实例。这种技术称为模拟多重继承,与[Item-18](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-18-Favor-composition-over-inheritance.md)中讨论的包装类密切相关。它提供了多重继承的许多好处,同时避免了缺陷。 Writing a skeletal implementation is a relatively simple, if somewhat tedious, process. First, study the interface and decide which methods are the primitives in terms of which the others can be implemented. These primitives will be the abstract methods in your skeletal implementation. Next, provide default methods in the interface for all of the methods that can be implemented directly atop the primitives, but recall that you may not provide default methods for Object methods such as equals and hashCode. If the primitives and default methods cover the interface, you’re done, and have no need for a skeletal implementation class. Otherwise, write a class declared to implement the interface, with implementations of all of the remaining interface methods. The class may contain any nonpublic fields ands methods appropriate to the task. @@ -148,7 +148,7 @@ Note that this skeletal implementation could not be implemented in the Map.Entry Because skeletal implementations are designed for inheritance, you should follow all of the design and documentation guidelines in Item 19. For brevity’s sake, the documentation comments were omitted from the previous example, but good documentation is absolutely essential in a skeletal implementation, whether it consists of default methods on an interface or a separate abstract class. -因为骨架实现是为继承而设计的,所以您应该遵循[Item-19](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-19-Design-and-document-for-inheritance-or-else-prohibit-it.md)中的所有设计和文档指南。为了简洁起见,在前面的示例中省略了文档注释,但是优秀的文档对于骨架实现来说是绝对必要的,不管它是由接口上的默认方法还是单独的抽象类组成。 +因为骨架实现是为继承而设计的,所以您应该遵循[Item-19](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-19-Design-and-document-for-inheritance-or-else-prohibit-it.md)中的所有设计和文档指南。为了简洁起见,在前面的示例中省略了文档注释,但是优秀的文档对于骨架实现来说是绝对必要的,不管它是由接口上的默认方法还是单独的抽象类组成。 A minor variant on the skeletal implementation is the simple implementation, exemplified by AbstractMap.SimpleEntry. A simple implementation is like a skeletal implementation in that it implements an interface and is designed for inheritance, but it differs in that it isn’t abstract: it is the simplest possible working implementation. You can use it as it stands or subclass it as circumstances warrant. diff --git a/Chapter-4-Item-21-Design-interfaces-for-posterity.md b/Chapter-4/Chapter-4-Item-21-Design-interfaces-for-posterity.md similarity index 97% rename from Chapter-4-Item-21-Design-interfaces-for-posterity.md rename to Chapter-4/Chapter-4-Item-21-Design-interfaces-for-posterity.md index 53007ac..e7babda 100644 --- a/Chapter-4-Item-21-Design-interfaces-for-posterity.md +++ b/Chapter-4/Chapter-4-Item-21-Design-interfaces-for-posterity.md @@ -35,7 +35,7 @@ default boolean removeif(predicate filter) { This is the best general-purpose implementation one could possibly write for the removeIf method, but sadly, it fails on some real-world Collection implementations. For example, consider org.apache.commons.collections4.collection.SynchronizedCollection. This class, from the Apache Commons library, is similar to the one returned by the static factory Collections.-synchronizedCollection in java.util. The Apache version additionally provides the ability to use a client-supplied object for locking, in place of the collection. In other words, it is a wrapper class (Item 18), all of whose methods synchronize on a locking object before delegating to the wrapped collection. -这是为removeIf方法编写的最好的通用实现,但遗憾的是,它在一些实际的集合实现中失败了。例如,考虑org.apache.commons.collections4.collection.SynchronizedCollection。这个类来自Apache Commons库,类似于静态工厂集合返回的类。-synchronizedCollection java.util。Apache版本还提供了使用客户机提供的对象进行锁定的功能,以代替集合。换句话说,它是一个包装器类([Item-18](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-18-Favor-composition-over-inheritance.md)),其所有方法在委托给包装集合之前同步锁定对象。 +这是为removeIf方法编写的最好的通用实现,但遗憾的是,它在一些实际的集合实现中失败了。例如,考虑org.apache.commons.collections4.collection.SynchronizedCollection。这个类来自Apache Commons库,类似于静态工厂集合返回的类。-synchronizedCollection java.util。Apache版本还提供了使用客户机提供的对象进行锁定的功能,以代替集合。换句话说,它是一个包装器类([Item-18](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-18-Favor-composition-over-inheritance.md)),其所有方法在委托给包装集合之前同步锁定对象。 The Apache SynchronizedCollection class is still being actively maintained, but as of this writing, it does not override the removeIf method. If this class is used in conjunction with Java 8, it will therefore inherit the default implementation of removeIf, which does not, indeed cannot, maintain the class’s fundamental promise: to automatically synchronize around each method invocation. The default implementation knows nothing about synchronization and has no access to the field that contains the locking object. If a client calls the removeIf method on a SynchronizedCollection instance in the presence of concurrent modification of the collection by another thread, a ConcurrentModificationException or other unspecified behavior may result. @@ -51,7 +51,7 @@ In order to prevent this from happening in similar Java platform libraries imple Using default methods to add new methods to existing interfaces should be avoided unless the need is critical, in which case you should think long and hard about whether an existing interface implementation might be broken by your default method implementation. Default methods are, however, extremely useful for providing standard method implementations when an interface is created, to ease the task of implementing the interface (Item 20). -除非必要,否则应该避免使用默认方法向现有接口添加新方法,在这种情况下,您应该仔细考虑现有接口实现是否可能被默认方法破坏。然而,在创建接口时,默认方法对于提供标准方法实现非常有用,以减轻实现接口的任务([Item-20](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-20-Prefer-interfaces-to-abstract-classes.md))。 +除非必要,否则应该避免使用默认方法向现有接口添加新方法,在这种情况下,您应该仔细考虑现有接口实现是否可能被默认方法破坏。然而,在创建接口时,默认方法对于提供标准方法实现非常有用,以减轻实现接口的任务([Item-20](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-20-Prefer-interfaces-to-abstract-classes.md))。 It is also worth noting that default methods were not designed to support removing methods from interfaces or changing the signatures of existing methods. Neither of these interface changes is possible without breaking existing clients. diff --git a/Chapter-4-Item-22-Use-interfaces-only-to-define-types.md b/Chapter-4/Chapter-4-Item-22-Use-interfaces-only-to-define-types.md similarity index 94% rename from Chapter-4-Item-22-Use-interfaces-only-to-define-types.md rename to Chapter-4/Chapter-4-Item-22-Use-interfaces-only-to-define-types.md index 8a4d5f5..6a9e53d 100644 --- a/Chapter-4-Item-22-Use-interfaces-only-to-define-types.md +++ b/Chapter-4/Chapter-4-Item-22-Use-interfaces-only-to-define-types.md @@ -34,7 +34,7 @@ Java平台库中有几个常量接口,例如java.io.ObjectStreamConstants。 If you want to export constants, there are several reasonable choices. If the constants are strongly tied to an existing class or interface, you should add them to the class or interface. For example, all of the boxed numerical primitive classes, such as Integer and Double, export MIN_VALUE and MAX_VALUE constants. If the constants are best viewed as members of an enumerated type, you should export them with an enum type (Item 34). Otherwise, you should export the constants with a noninstantiable utility class (Item 4). Here is a utility class version of the PhysicalConstants example shown earlier: -如果您想导出常量,有几个合理的选择。如果这些常量与现有的类或接口紧密绑定,则应该将它们添加到类或接口。例如,所有装箱的数值包装类,比如Integer和Double,都导出MIN_VALUE和MAX_VALUE常量。如果最好将这些常量看作枚举类型的成员,那么应该使用enum类型导出它们([Item-34](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-6-Item-34-Use-enums-instead-of-int-constants.md))。否则,您应该使用不可实例化的工具类([Item-4](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2-Item-4-Enforce-noninstantiability-with-a-private-constructor.md))导出常量。下面是一个之前的PhysicalConstants例子的工具类另一个版本: +如果您想导出常量,有几个合理的选择。如果这些常量与现有的类或接口紧密绑定,则应该将它们添加到类或接口。例如,所有装箱的数值包装类,比如Integer和Double,都导出MIN_VALUE和MAX_VALUE常量。如果最好将这些常量看作枚举类型的成员,那么应该使用enum类型导出它们([Item-34](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-6/Chapter-6-Item-34-Use-enums-instead-of-int-constants.md))。否则,您应该使用不可实例化的工具类([Item-4](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2/Chapter-2-Item-4-Enforce-noninstantiability-with-a-private-constructor.md))导出常量。下面是一个之前的PhysicalConstants例子的工具类另一个版本: ``` // Constant utility class diff --git a/Chapter-4-Item-23-Prefer-class-hierarchies-to-tagged-classes.md b/Chapter-4/Chapter-4-Item-23-Prefer-class-hierarchies-to-tagged-classes.md similarity index 98% rename from Chapter-4-Item-23-Prefer-class-hierarchies-to-tagged-classes.md rename to Chapter-4/Chapter-4-Item-23-Prefer-class-hierarchies-to-tagged-classes.md index 5390809..9dd78d3 100644 --- a/Chapter-4-Item-23-Prefer-class-hierarchies-to-tagged-classes.md +++ b/Chapter-4/Chapter-4-Item-23-Prefer-class-hierarchies-to-tagged-classes.md @@ -113,7 +113,7 @@ class Square extends Rectangle { Note that the fields in the above hierarchy are accessed directly rather than by accessor methods. This was done for brevity and would be a poor design if the hierarchy were public (Item 16). -注意,上面层次结构中的字段是直接访问的,而不是通过访问器方法访问的。这样做是为了简洁,如果层次结构是公共的,那么这将是一个糟糕的设计([Item-16](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-16-In-public-classes-use-accessor-methods-not-public-fields.md))。 +注意,上面层次结构中的字段是直接访问的,而不是通过访问器方法访问的。这样做是为了简洁,如果层次结构是公共的,那么这将是一个糟糕的设计([Item-16](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-16-In-public-classes-use-accessor-methods-not-public-fields.md))。 In summary, tagged classes are seldom appropriate. If you’re tempted to write a class with an explicit tag field, think about whether the tag could be eliminated and the class replaced by a hierarchy. When you encounter an existing class with a tag field, consider refactoring it into a hierarchy. diff --git a/Chapter-4-Item-24-Favor-static-member-classes-over-nonstatic.md b/Chapter-4/Chapter-4-Item-24-Favor-static-member-classes-over-nonstatic.md similarity index 94% rename from Chapter-4-Item-24-Favor-static-member-classes-over-nonstatic.md rename to Chapter-4/Chapter-4-Item-24-Favor-static-member-classes-over-nonstatic.md index 9c07f59..40b4d4b 100644 --- a/Chapter-4-Item-24-Favor-static-member-classes-over-nonstatic.md +++ b/Chapter-4/Chapter-4-Item-24-Favor-static-member-classes-over-nonstatic.md @@ -12,7 +12,7 @@ A static member class is the simplest kind of nested class. It is best thought o One common use of a static member class is as a public helper class, useful only in conjunction(n. 结合;[语] 连接词;同时发生) with its outer class. For example, consider an enum describing the operations supported by a calculator (Item 34). The Operation enum should be a public static member class of the Calculator class. Clients of Calculator could then refer to operations using names like Calculator.Operation.PLUS and Calculator.Operation.MINUS. -静态成员类的一个常见用法是作为公有的辅助类,只有与它的外部类一起使用时才有意义。例如,考虑一个描述了计算器支持的各种操作的枚举([Item-34](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-6-Item-34-Use-enums-instead-of-int-constants.md))。Operation枚举应该是Calculator类的公有静态成员类,Calculator类的客户端就可以用Calculator.Operation.PLUS和Calculator.Operation.MINUS等名称来引用这些操作。 +静态成员类的一个常见用法是作为公有的辅助类,只有与它的外部类一起使用时才有意义。例如,考虑一个描述了计算器支持的各种操作的枚举([Item-34](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-6/Chapter-6-Item-34-Use-enums-instead-of-int-constants.md))。Operation枚举应该是Calculator类的公有静态成员类,Calculator类的客户端就可以用Calculator.Operation.PLUS和Calculator.Operation.MINUS等名称来引用这些操作。 Syntactically(adv. 依照句法地;在语句构成上), the only difference between static and nonstatic member classes is that static member classes have the modifier static in their declarations. Despite(n. 轻视;憎恨;侮辱;prep. 尽管,不管) the syntactic similarity, these two kinds of nested classes are very different. Each instance of a nonstatic member class is implicitly associated with an enclosing instance of its containing class. Within instance methods of a nonstatic member class, you can invoke methods on the enclosing instance or obtain a reference to the enclosing instance using the qualified this construct [JLS, 15.8.4]. If an instance of a nested class can exist in isolation from an instance of its enclosing class, then the nested class must be a static member class: it is impossible to create an instance of a nonstatic member class without an enclosing instance. @@ -42,7 +42,7 @@ public class MySet extends AbstractSet { **If you declare a member class that does not require access to an enclosing instance, always put the static modifier in its declaration,** making it a static rather than a nonstatic member class. If you omit this modifier, each instance will have a hidden extraneous reference to its enclosing instance. As previously mentioned, storing this reference takes time and space. More seriously, it can result in the enclosing instance being retained when it would otherwise be eligible for garbage collection (Item 7). The resulting memory leak can be catastrophic. It is often difficult to detect because the reference is invisible. -**如果声明的成员类不需要访问外部的实例,那么(应)始终在声明中添加static修饰符,使其成为静态的而不是非静态的成员类。** 如果省略这个修饰符,每个实例都有一个隐藏的对其外部实例的额外引用。如前所述,存储此引用需要时间和空间。更严重的是,它可能会在(满足)进行垃圾收集(条件)时(仍)保留外部类的实例([Item-7](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2-Item-7-Eliminate-obsolete-object-references.md))。由于引用是不可见的,因此通常很难检测到。 +**如果声明的成员类不需要访问外部的实例,那么(应)始终在声明中添加static修饰符,使其成为静态的而不是非静态的成员类。** 如果省略这个修饰符,每个实例都有一个隐藏的对其外部实例的额外引用。如前所述,存储此引用需要时间和空间。更严重的是,它可能会在(满足)进行垃圾收集(条件)时(仍)保留外部类的实例([Item-7](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2/Chapter-2-Item-7-Eliminate-obsolete-object-references.md))。由于引用是不可见的,因此通常很难检测到。 A common use of private static member classes is to represent components of the object represented by their enclosing class. For example, consider a Map instance, which associates keys with values. Many Map implementations have an internal Entry object for each key-value pair in the map. While each entry is associated with a map, the methods on an entry (getKey, getValue, and setValue) do not need access to the map. Therefore, it would be wasteful to use a nonstatic member class to represent entries: a private static member class is best. If you accidentally omit the static modifier in the entry declaration, the map will still work, but each entry will contain a superfluous reference to the map, which wastes space and time. @@ -62,7 +62,7 @@ There are many limitations on the applicability of anonymous classes. You can’ Before lambdas were added to Java (Chapter 6), anonymous classes were the preferred means of creating small function objects and process objects on the fly, but lambdas are now preferred (Item 42). Another common use of anonymous classes is in the implementation of static factory methods (see intArrayAsList in Item 20). -在lambdas被添加到Java(第6章)之前,匿名类是动态创建小函数对象和进程对象的首选方法,但lambdas现在是首选方法([Item-42](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-7-Item-42-Prefer-lambdas-to-anonymous-classes.md))。匿名类的另一个常见用法是实现静态工厂方法(参见[Item-20](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-20-Prefer-interfaces-to-abstract-classes.md)中的intArrayAsList)。 +在lambdas被添加到Java(第6章)之前,匿名类是动态创建小函数对象和进程对象的首选方法,但lambdas现在是首选方法([Item-42](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-7/Chapter-7-Item-42-Prefer-lambdas-to-anonymous-classes.md))。匿名类的另一个常见用法是实现静态工厂方法(参见[Item-20](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-20-Prefer-interfaces-to-abstract-classes.md)中的intArrayAsList)。 Local classes are the least frequently used of the four kinds of nested classes. A local class can be declared practically anywhere a local variable can be declared and obeys the same scoping rules. Local classes have attributes in common with each of the other kinds of nested classes. Like member classes, they have names and can be used repeatedly. Like anonymous classes, they have enclosing instances only if they are defined in a nonstatic context, and they cannot contain static members. And like anonymous classes, they should be kept short so as not to harm readability. diff --git a/Chapter-4-Item-25-Limit-source-files-to-a-single-top-level-class.md b/Chapter-4/Chapter-4-Item-25-Limit-source-files-to-a-single-top-level-class.md similarity index 91% rename from Chapter-4-Item-25-Limit-source-files-to-a-single-top-level-class.md rename to Chapter-4/Chapter-4-Item-25-Limit-source-files-to-a-single-top-level-class.md index fe50f89..5e22004 100644 --- a/Chapter-4-Item-25-Limit-source-files-to-a-single-top-level-class.md +++ b/Chapter-4/Chapter-4-Item-25-Limit-source-files-to-a-single-top-level-class.md @@ -56,7 +56,7 @@ If you compile the program with the command javac Main.java or javac Main.java U Fixing the problem is as simple as splitting the top-level classes (Utensil and Dessert, in the case of our example) into separate source files. If you are tempted to put multiple top-level classes into a single source file, consider using static member classes (Item 24) as an alternative to splitting the classes into separate source files. If the classes are subservient to another class, making them into static member classes is generally the better alternative because it enhances readability and makes it possible to reduce the accessibility of the classes by declaring them private (Item 15). Here is how our example looks with static member classes: -修复这个问题非常简单,只需将顶层类(在我们的示例中是餐具和甜点)分割为单独的源文件即可。如果您想将多个顶层类放到一个源文件中,请考虑使用静态成员类([Item-24](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-24-Favor-static-member-classes-over-nonstatic.md))作为将类分割为单独的源文件的替代方法。如果(多个顶层类)隶属于另一个类,那么将它们转换成静态成员类通常是更好的选择,因为它增强了可读性,并通过声明它们为私有([Item-15](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-15-Minimize-the-accessibility-of-classes-and-members.md)),从而降低了类的可访问性。下面是我们的静态成员类示例的样子: +修复这个问题非常简单,只需将顶层类(在我们的示例中是餐具和甜点)分割为单独的源文件即可。如果您想将多个顶层类放到一个源文件中,请考虑使用静态成员类([Item-24](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-24-Favor-static-member-classes-over-nonstatic.md))作为将类分割为单独的源文件的替代方法。如果(多个顶层类)隶属于另一个类,那么将它们转换成静态成员类通常是更好的选择,因为它增强了可读性,并通过声明它们为私有([Item-15](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-15-Minimize-the-accessibility-of-classes-and-members.md)),从而降低了类的可访问性。下面是我们的静态成员类示例的样子: ``` // Static member classes instead of multiple top-level classes diff --git a/Chapter-5-Introduction.md b/Chapter-5/Chapter-5-Introduction.md similarity index 100% rename from Chapter-5-Introduction.md rename to Chapter-5/Chapter-5-Introduction.md diff --git a/Chapter-5-Item-26-Do-not-use-raw-types.md b/Chapter-5/Chapter-5-Item-26-Do-not-use-raw-types.md similarity index 91% rename from Chapter-5-Item-26-Do-not-use-raw-types.md rename to Chapter-5/Chapter-5-Item-26-Do-not-use-raw-types.md index 870e6cb..f88000f 100644 --- a/Chapter-5-Item-26-Do-not-use-raw-types.md +++ b/Chapter-5/Chapter-5-Item-26-Do-not-use-raw-types.md @@ -59,7 +59,7 @@ private final Collection stamps = ... ; From this declaration, the compiler knows that stamps should contain only Stamp instances and guarantees it to be true, assuming your entire codebase compiles without emitting (or suppressing; see Item 27) any warnings. When stamps is declared with a parameterized type declaration, the erroneous insertion generates a compile-time error message that tells you exactly what is wrong: -从这个声明看出,编译器应该知道 stamps 应该只包含 Stamp 实例,为保证它确实如此,假设你的整个代码库编译没有发出(或抑制;详见 [Item-27](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-27-Eliminate-unchecked-warnings.md))任何警告。当 stamps 利用一个参数化的类型进行声明时,错误的插入将生成编译时错误消息,该消息将确切地告诉你哪里出了问题: +从这个声明看出,编译器应该知道 stamps 应该只包含 Stamp 实例,为保证它确实如此,假设你的整个代码库编译没有发出(或抑制;详见 [Item-27](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-27-Eliminate-unchecked-warnings.md))任何警告。当 stamps 利用一个参数化的类型进行声明时,错误的插入将生成编译时错误消息,该消息将确切地告诉你哪里出了问题: ``` Test.java:9: error: incompatible types: Coin cannot be converted @@ -74,11 +74,11 @@ The compiler inserts invisible casts for you when retrieving(v. [计] 检索 As noted earlier, it is legal to use raw types (generic types without their type parameters), but you should never do it. **If you use raw types, you lose all the safety and expressiveness benefits of generics.** Given that you shouldn’t use them, why did the language designers permit raw types in the first place? For compatibility. Java was about to enter its second decade when generics were added, and there was an enormous amount of code in existence that did not use generics. It was deemed critical that all of this code remain legal and interoperate with newer code that does use generics. It had to be legal to pass instances of parameterized types to methods that were designed for use with raw types, and vice versa. This requirement, known as migration compatibility, drove the decisions to support raw types and to implement generics using erasure (Item 28). -如前所述,使用原始类型(没有类型参数的泛型)是合法的,但是你永远不应该这样做。**如果使用原始类型,就会失去泛型的安全性和表现力。** 既然你不应该使用它们,那么为什么语言设计者一开始就允许原始类型呢?答案是:为了兼容性。Java 即将进入第二个十年,泛型被添加进来时,还存在大量不使用泛型的代码。保持所有这些代码合法并与使用泛型的新代码兼容被认为是关键的。将参数化类型的实例传递给设计用于原始类型的方法必须是合法的,反之亦然。这被称为迁移兼容性的需求,它促使原始类型得到支持并使用擦除实现泛型 ([Item-28](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-28-Prefer-lists-to-arrays.md))。 +如前所述,使用原始类型(没有类型参数的泛型)是合法的,但是你永远不应该这样做。**如果使用原始类型,就会失去泛型的安全性和表现力。** 既然你不应该使用它们,那么为什么语言设计者一开始就允许原始类型呢?答案是:为了兼容性。Java 即将进入第二个十年,泛型被添加进来时,还存在大量不使用泛型的代码。保持所有这些代码合法并与使用泛型的新代码兼容被认为是关键的。将参数化类型的实例传递给设计用于原始类型的方法必须是合法的,反之亦然。这被称为迁移兼容性的需求,它促使原始类型得到支持并使用擦除实现泛型 ([Item-28](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-28-Prefer-lists-to-arrays.md))。 While you shouldn’t use raw types such as List, it is fine to use types that are parameterized to allow insertion of arbitrary objects, such as `List`. Just what is the difference between the raw type List and the parameterized type `List`? Loosely speaking, the former has opted out of the generic type system, while the latter has explicitly told the compiler that it is capable of holding objects of any type. While you can pass a `List` to a parameter of type List, you can’t pass it to a parameter of type `List`. There are sub-typing rules for generics, and `List` is a subtype of the raw type List, but not of the parameterized type `List` (Item 28). As a consequence, **you lose type safety if you use a raw type such as List, but not if you use a parameterized type such as List<Object>.** -虽然你不应该使用原始类型(如 List),但是可以使用参数化的类型来允许插入任意对象,如 `List`。原始类型 List 和参数化类型 `List` 之间的区别是什么?粗略地说,前者选择了不使用泛型系统,而后者明确地告诉编译器它能够保存任何类型的对象。虽然可以将 `List` 传递给 List 类型的参数,但不能将其传递给类型 `List` 的参数。泛型有子类型规则,`List` 是原始类型 List 的子类型,而不是参数化类型 `List` 的子类型([Item-28](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-28-Prefer-lists-to-arrays.md))。因此,**如果使用原始类型(如 List),就会失去类型安全性,但如果使用参数化类型(如 `List`)则不会。** +虽然你不应该使用原始类型(如 List),但是可以使用参数化的类型来允许插入任意对象,如 `List`。原始类型 List 和参数化类型 `List` 之间的区别是什么?粗略地说,前者选择了不使用泛型系统,而后者明确地告诉编译器它能够保存任何类型的对象。虽然可以将 `List` 传递给 List 类型的参数,但不能将其传递给类型 `List` 的参数。泛型有子类型规则,`List` 是原始类型 List 的子类型,而不是参数化类型 `List` 的子类型([Item-28](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-28-Prefer-lists-to-arrays.md))。因此,**如果使用原始类型(如 List),就会失去类型安全性,但如果使用参数化类型(如 `List`)则不会。** To make this concrete, consider the following program: @@ -162,7 +162,7 @@ CAP#1 extends Object from capture of ? Admittedly this error message leaves something to be desired, but the compiler has done its job, preventing you from corrupting(vt. 使腐烂;使堕落,使恶化) the collection’s type invariant(n. [数] 不变量;[计] 不变式), whatever its element type may be. Not only can’t you put any element (other than null) into a `Collection`, but you can’t assume anything about the type of the objects that you get out. If these restrictions are unacceptable, you can use generic methods (Item 30) or bounded wildcard types (Item 31). -无可否认,这个错误消息让人不满意,但是编译器已经完成了它的工作,防止你无视它的元素类型而破坏集合的类型一致性。你不仅不能将任何元素(除 null 之外)放入 `Collection`,而且不能臆想你得到的对象的类型。如果这些限制是不可接受的,你可以使用泛型方法([Item-30](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-30-Favor-generic-methods.md))或有界通配符类型([Item-31](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-31-Use-bounded-wildcards-to-increase-API-flexibility.md))。 +无可否认,这个错误消息让人不满意,但是编译器已经完成了它的工作,防止你无视它的元素类型而破坏集合的类型一致性。你不仅不能将任何元素(除 null 之外)放入 `Collection`,而且不能臆想你得到的对象的类型。如果这些限制是不可接受的,你可以使用泛型方法([Item-30](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-30-Favor-generic-methods.md))或有界通配符类型([Item-31](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-31-Use-bounded-wildcards-to-increase-API-flexibility.md))。 There are a few minor exceptions to the rule that you should not use raw types. **You must use raw types in class literals.** The specification does not permit the use of parameterized types (though it does permit array types and primitive types) [JLS, 15.8.2]. In other words, List.class, String[].class, and int.class are all legal, but `List.class` and `List.class` are not. @@ -194,14 +194,14 @@ For quick reference, the terms introduced in this item (and a few introduced lat | Term | Example | Item | |:-------:|:-------:|:-------:| -| Parameterized type | `List` | [Item-26](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-26-Do-not-use-raw-types.md) | -| Actual type parameter | `String` | [Item-26](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-26-Do-not-use-raw-types.md) | -| Generic type | `List` | [Item-26](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-26-Do-not-use-raw-types.md), [Item-29](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-29-Favor-generic-types.md) | -| Formal type parameter | `E` | [Item-26](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-26-Do-not-use-raw-types.md) | -| Unbounded wildcard type | `List` | [Item-26](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-26-Do-not-use-raw-types.md) | -| Raw type | `List` | [Item-26](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-26-Do-not-use-raw-types.md) | -| Bounded type parameter | `` | [Item-29](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-29-Favor-generic-types.md) | -| Recursive type bound | `>` | [Item-30](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-30-Favor-generic-methods.md) | -| Bounded wildcard type | `List` | [Item-31](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-31-Use-bounded-wildcards-to-increase-API-flexibility.md) | -| Generic method | `static List asList(E[] a)` | [Item-30](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-30-Favor-generic-methods.md) | -| Type token | `String.class` | [Item-33](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-33-Consider-typesafe-heterogeneous-containers.md) | +| Parameterized type | `List` | [Item-26](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-26-Do-not-use-raw-types.md) | +| Actual type parameter | `String` | [Item-26](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-26-Do-not-use-raw-types.md) | +| Generic type | `List` | [Item-26](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-26-Do-not-use-raw-types.md), [Item-29](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-29-Favor-generic-types.md) | +| Formal type parameter | `E` | [Item-26](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-26-Do-not-use-raw-types.md) | +| Unbounded wildcard type | `List` | [Item-26](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-26-Do-not-use-raw-types.md) | +| Raw type | `List` | [Item-26](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-26-Do-not-use-raw-types.md) | +| Bounded type parameter | `` | [Item-29](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-29-Favor-generic-types.md) | +| Recursive type bound | `>` | [Item-30](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-30-Favor-generic-methods.md) | +| Bounded wildcard type | `List` | [Item-31](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-31-Use-bounded-wildcards-to-increase-API-flexibility.md) | +| Generic method | `static List asList(E[] a)` | [Item-30](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-30-Favor-generic-methods.md) | +| Type token | `String.class` | [Item-33](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-33-Consider-typesafe-heterogeneous-containers.md) | diff --git a/Chapter-5-Item-27-Eliminate-unchecked-warnings.md b/Chapter-5/Chapter-5-Item-27-Eliminate-unchecked-warnings.md similarity index 100% rename from Chapter-5-Item-27-Eliminate-unchecked-warnings.md rename to Chapter-5/Chapter-5-Item-27-Eliminate-unchecked-warnings.md diff --git a/Chapter-5-Item-28-Prefer-lists-to-arrays.md b/Chapter-5/Chapter-5-Item-28-Prefer-lists-to-arrays.md similarity index 91% rename from Chapter-5-Item-28-Prefer-lists-to-arrays.md rename to Chapter-5/Chapter-5-Item-28-Prefer-lists-to-arrays.md index c7c5640..c57f6fb 100644 --- a/Chapter-5-Item-28-Prefer-lists-to-arrays.md +++ b/Chapter-5/Chapter-5-Item-28-Prefer-lists-to-arrays.md @@ -28,7 +28,7 @@ Either way you can’t put a String into a Long container, but with an array you The second major difference between arrays and generics is that arrays are reified(adj. 具体化的) [JLS, 4.7]. This means that arrays know and enforce their element type at runtime. As noted earlier, if you try to put a String into an array of Long, you’ll get an ArrayStoreException. Generics, by contrast, are implemented by erasure [JLS, 4.6]. This means that they enforce their type constraints only at compile time and discard (or erase) their element type information at runtime. Erasure is what allowed generic types to interoperate freely with legacy code that didn’t use generics (Item 26), ensuring a smooth(adj. 顺利的;光滑的;平稳的) transition to generics in Java 5. -数组和泛型之间的第二个主要区别:数组是具体化的 [JLS, 4.7]。这意味着数组在运行时知道并强制执行他们的元素类型。如前所述,如果试图将 String 元素放入一个 Long 类型的数组中,就会得到 ArrayStoreException。相比之下,泛型是通过擦除来实现的 [JLS, 4.6]。这意味着它们只在编译时执行类型约束,并在运行时丢弃(或擦除)元素类型信息。擦除允许泛型与不使用泛型的遗留代码自由交互操作([Item-26](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-26-Do-not-use-raw-types.md)),确保在 Java 5 中平稳地过渡。 +数组和泛型之间的第二个主要区别:数组是具体化的 [JLS, 4.7]。这意味着数组在运行时知道并强制执行他们的元素类型。如前所述,如果试图将 String 元素放入一个 Long 类型的数组中,就会得到 ArrayStoreException。相比之下,泛型是通过擦除来实现的 [JLS, 4.6]。这意味着它们只在编译时执行类型约束,并在运行时丢弃(或擦除)元素类型信息。擦除允许泛型与不使用泛型的遗留代码自由交互操作([Item-26](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-26-Do-not-use-raw-types.md)),确保在 Java 5 中平稳地过渡。 Because of these fundamental(adj. 基本的,根本的) differences, arrays and generics do not mix well. For example, it is illegal to create an array of a generic type, a parameterized type, or a type parameter. Therefore, none of these array creation expressions are legal: `new List[]`, `new List[]`, `new E[]`. All will result in generic array creation errors at compile time. @@ -57,11 +57,11 @@ Let’s pretend that line 1, which creates a generic array, is legal. Line 2 cre Types such as E, `List`, and `List` are technically known as nonreifiable types [JLS, 4.7]. Intuitively speaking, a non-reifiable type is one whose runtime representation contains less information than its compile-time representation. Because of erasure, the only parameterized types that are reifiable are unbounded wildcard types such as `List` and `Map` (Item 26). It is legal, though rarely useful, to create arrays of unbounded wildcard types. -E、`List` 和 `List` 等类型在技术上称为不可具体化类型 [JLS, 4.7]。直观地说,非具体化类型的运行时表示包含的信息少于其编译时表示。由于擦除,唯一可具体化的参数化类型是无限制通配符类型,如 `List` 和 `Map`([Item-26](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-26-Do-not-use-raw-types.md))。创建无边界通配符类型数组是合法的,但不怎么有用。 +E、`List` 和 `List` 等类型在技术上称为不可具体化类型 [JLS, 4.7]。直观地说,非具体化类型的运行时表示包含的信息少于其编译时表示。由于擦除,唯一可具体化的参数化类型是无限制通配符类型,如 `List` 和 `Map`([Item-26](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-26-Do-not-use-raw-types.md))。创建无边界通配符类型数组是合法的,但不怎么有用。 The prohibition on generic array creation can be annoying. It means, for example, that it’s not generally possible for a generic collection to return an array of its element type (but see Item 33 for a partial solution). It also means that you get confusing(adj. 混乱的;混淆的;令人困惑的) warnings when using varargs methods (Item 53) in combination with generic types. This is because every time you invoke a varargs method, an array is created to hold the varargs parameters. If the element type of this array is not reifiable, you get a warning. The SafeVarargs annotation can be used to address this issue (Item 32). -禁止创建泛型数组可能很烦人。例如,这意味着泛型集合通常不可能返回其元素类型的数组(部分解决方案请参见 [Item-33](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-33-Consider-typesafe-heterogeneous-containers.md))。这也意味着在使用 varargs 方法([Item-53](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-8-Item-53-Use-varargs-judiciously.md))与泛型组合时,你会得到令人困惑的警告。这是因为每次调用 varargs 方法时,都会创建一个数组来保存 varargs 参数。如果该数组的元素类型不可具体化,则会得到警告。SafeVarargs 注解可以用来解决这个问题([Item-32](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-32-Combine-generics-and-varargs-judiciously.md))。 +禁止创建泛型数组可能很烦人。例如,这意味着泛型集合通常不可能返回其元素类型的数组(部分解决方案请参见 [Item-33](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-33-Consider-typesafe-heterogeneous-containers.md))。这也意味着在使用 varargs 方法([Item-53](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-8/Chapter-8-Item-53-Use-varargs-judiciously.md))与泛型组合时,你会得到令人困惑的警告。这是因为每次调用 varargs 方法时,都会创建一个数组来保存 varargs 参数。如果该数组的元素类型不可具体化,则会得到警告。SafeVarargs 注解可以用来解决这个问题([Item-32](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-32-Combine-generics-and-varargs-judiciously.md))。 ***译注:varargs 方法,指带有可变参数的方法。*** @@ -91,7 +91,7 @@ public class Chooser { To use this class, you have to cast the choose method’s return value from Object to the desired type every time you use invoke the method, and the cast will fail at runtime if you get the type wrong. Taking the advice of Item 29 to heart, we attempt to modify Chooser to make it generic. Changes are shown in boldface: -要使用这个类,每次使用方法调用时,必须将 choose 方法的返回值从对象转换为所需的类型,如果类型错误,转换将在运行时失败。我们认真考虑了 [Item-29](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-29-Favor-generic-types.md) 的建议,试图对 Chooser 进行修改,使其具有通用性。变化以粗体显示: +要使用这个类,每次使用方法调用时,必须将 choose 方法的返回值从对象转换为所需的类型,如果类型错误,转换将在运行时失败。我们认真考虑了 [Item-29](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-29-Favor-generic-types.md) 的建议,试图对 Chooser 进行修改,使其具有通用性。变化以粗体显示: ``` // A first cut at making Chooser generic - won't compile @@ -138,7 +138,7 @@ T extends Object declared in class Chooser The compiler is telling you that it can’t vouch for the safety of the cast at runtime because the program won’t know what type T represents—remember, element type information is erased from generics at runtime. Will the program work? Yes, but the compiler can’t prove it. You could prove it to yourself, put the proof in a comment and suppress the warning with an annotation, but you’re better off eliminating the cause of warning (Item 27). -编译器告诉你,它不能保证在运行时转换的安全性,因为程序不知道类型 T 代表什么。记住,元素类型信息在运行时从泛型中删除。这个计划会奏效吗?是的,但是编译器不能证明它。你可以向自己证明这一点,但是你最好将证据放在注释中,指出消除警告的原因([Item-27](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-27-Eliminate-unchecked-warnings.md)),并使用注解隐藏警告。 +编译器告诉你,它不能保证在运行时转换的安全性,因为程序不知道类型 T 代表什么。记住,元素类型信息在运行时从泛型中删除。这个计划会奏效吗?是的,但是编译器不能证明它。你可以向自己证明这一点,但是你最好将证据放在注释中,指出消除警告的原因([Item-27](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-27-Eliminate-unchecked-warnings.md)),并使用注解隐藏警告。 To eliminate the unchecked cast warning, use a list instead of an array. Here is a version of the Chooser class that compiles without error or warning: diff --git a/Chapter-5-Item-29-Favor-generic-types.md b/Chapter-5/Chapter-5-Item-29-Favor-generic-types.md similarity index 83% rename from Chapter-5-Item-29-Favor-generic-types.md rename to Chapter-5/Chapter-5-Item-29-Favor-generic-types.md index e49758e..7a97315 100644 --- a/Chapter-5-Item-29-Favor-generic-types.md +++ b/Chapter-5/Chapter-5-Item-29-Favor-generic-types.md @@ -8,7 +8,7 @@ It is generally not too difficult to parameterize your declarations and make use Consider the simple (toy) stack implementation from Item 7: -考虑 [Item-7](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2-Item-7-Eliminate-obsolete-object-references.md) 中简单的堆栈实现: +考虑 [Item-7](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2/Chapter-2-Item-7-Eliminate-obsolete-object-references.md) 中简单的堆栈实现: ``` // Object-based collection - a prime candidate for generics @@ -47,7 +47,7 @@ public class Stack { This class should have been parameterized to begin with, but since it wasn’t, we can generify it after the fact. In other words, we can parameterize it without harming clients of the original non-parameterized version. As it stands, the client has to cast objects that are popped off the stack, and those casts might fail at runtime. The first step in generifying a class is to add one or more type parameters to its declaration. In this case there is one type parameter, representing the element type of the stack, and the conventional name for this type parameter is E (Item 68). -这个类一开始就应该是参数化的,但是因为它不是参数化的,所以我们可以在事后对它进行泛化。换句话说,我们可以对它进行参数化,而不会损害原始非参数化版本的客户端。按照目前的情况,客户机必须转换从堆栈中弹出的对象,而这些转换可能在运行时失败。生成类的第一步是向其声明中添加一个或多个类型参数。在这种情况下,有一个类型参数,表示堆栈的元素类型,这个类型参数的常规名称是 E([Item-68](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9-Item-68-Adhere-to-generally-accepted-naming-conventions.md))。 +这个类一开始就应该是参数化的,但是因为它不是参数化的,所以我们可以在事后对它进行泛化。换句话说,我们可以对它进行参数化,而不会损害原始非参数化版本的客户端。按照目前的情况,客户机必须转换从堆栈中弹出的对象,而这些转换可能在运行时失败。生成类的第一步是向其声明中添加一个或多个类型参数。在这种情况下,有一个类型参数,表示堆栈的元素类型,这个类型参数的常规名称是 E([Item-68](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9/Chapter-9-Item-68-Adhere-to-generally-accepted-naming-conventions.md))。 The next step is to replace all the uses of the type Object with the appropriate type parameter and then try to compile the resulting program: @@ -91,7 +91,7 @@ elements = new E[DEFAULT_INITIAL_CAPACITY]; As explained in Item 28, you can’t create an array of a non-reifiable type, such as E. This problem arises every time you write a generic type that is backed by an array. There are two reasonable ways to solve it. The first solution directly circumvents the prohibition on generic array creation: create an array of Object and cast it to the generic array type. Now in place of an error, the compiler will emit a warning. This usage is legal, but it’s not (in general) typesafe: -正如 [Item-28](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-28-Prefer-lists-to-arrays.md) 中所解释的,你不能创建非具体化类型的数组,例如 E。每当你编写由数组支持的泛型时,就会出现这个问题。有两种合理的方法来解决它。第一个解决方案直接绕过了创建泛型数组的禁令:创建对象数组并将其强制转换为泛型数组类型。现在,编译器将发出一个警告来代替错误。这种用法是合法的,但(一般而言)它不是类型安全的: +正如 [Item-28](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-28-Prefer-lists-to-arrays.md) 中所解释的,你不能创建非具体化类型的数组,例如 E。每当你编写由数组支持的泛型时,就会出现这个问题。有两种合理的方法来解决它。第一个解决方案直接绕过了创建泛型数组的禁令:创建对象数组并将其强制转换为泛型数组类型。现在,编译器将发出一个警告来代替错误。这种用法是合法的,但(一般而言)它不是类型安全的: ``` Stack.java:8: warning: [unchecked] unchecked cast @@ -106,7 +106,7 @@ The compiler may not be able to prove that your program is typesafe, but you can Once you’ve proved that an unchecked cast is safe, suppress the warning in as narrow a scope as possible (Item 27). In this case, the constructor contains only the unchecked array creation, so it’s appropriate to suppress the warning in the entire constructor. With the addition of an annotation to do this, Stack compiles cleanly, and you can use it without explicit casts or fear of a ClassCastException: -一旦你证明了 unchecked 的转换是安全的,就将警告限制在尽可能小的范围内([Item-27](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-27-Eliminate-unchecked-warnings.md))。在这种情况下,构造函数只包含 unchecked 的数组创建,因此在整个构造函数中取消警告是合适的。通过添加注解来实现这一点,Stack 可以干净地编译,而且你可以使用它而无需显式强制转换或担心 ClassCastException 异常: +一旦你证明了 unchecked 的转换是安全的,就将警告限制在尽可能小的范围内([Item-27](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-27-Eliminate-unchecked-warnings.md))。在这种情况下,构造函数只包含 unchecked 的数组创建,因此在整个构造函数中取消警告是合适的。通过添加注解来实现这一点,Stack 可以干净地编译,而且你可以使用它而无需显式强制转换或担心 ClassCastException 异常: ``` // The elements array will contain only E instances from push(E). @@ -142,7 +142,7 @@ E result = (E) elements[--size]; Because E is a non-reifiable type, there’s no way the compiler can check the cast at runtime. Again, you can easily prove to yourself that the unchecked cast is safe, so it’s appropriate to suppress the warning. In line with the advice of Item 27, we suppress the warning only on the assignment that contains the unchecked cast, not on the entire pop method: -因为 E 是不可具体化的类型,编译器无法在运行时检查强制转换。同样,你可以很容易地向自己证明 unchecked 的强制转换是安全的,因此可以适当地抑制警告。根据 [Item-27](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-27-Eliminate-unchecked-warnings.md) 的建议,我们仅对包含 unchecked 强制转换的赋值禁用警告,而不是对整个 pop 方法禁用警告: +因为 E 是不可具体化的类型,编译器无法在运行时检查强制转换。同样,你可以很容易地向自己证明 unchecked 的强制转换是安全的,因此可以适当地抑制警告。根据 [Item-27](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-27-Eliminate-unchecked-warnings.md) 的建议,我们仅对包含 unchecked 强制转换的赋值禁用警告,而不是对整个 pop 方法禁用警告: ``` // Appropriate suppression of unchecked warning @@ -159,7 +159,7 @@ public E pop() { Both techniques for eliminating the generic array creation have their adherents. The first is more readable: the array is declared to be of type E[], clearly indicating that it contains only E instances. It is also more concise: in a typical generic class, you read from the array at many points in the code; the first technique requires only a single cast (where the array is created), while the second requires a separate cast each time an array element is read. Thus, the first technique is preferable and more commonly used in practice. It does, however, cause heap pollution (Item 32): the runtime type of the array does not match its compile-time type (unless E happens to be Object). This makes some programmers sufficiently queasy that they opt for the second technique, though the heap pollution is harmless in this situation. -消除泛型数组创建的两种技术都有其追随者。第一个更容易读:数组声明为 E[] 类型,这清楚地表明它只包含 E 的实例。它也更简洁:在一个典型的泛型类中,从数组中读取代码中的许多点;第一种技术只需要一次转换(在创建数组的地方),而第二种技术在每次读取数组元素时都需要单独的转换。因此,第一种技术是可取的,在实践中更常用。但是,它确实会造成堆污染([Item-32](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-32-Combine-generics-and-varargs-judiciously.md)):数组的运行时类型与其编译时类型不匹配(除非 E 恰好是 Object)。尽管堆污染在这种情况下是无害的,但这使得一些程序员感到非常不安,因此他们选择了第二种技术。 +消除泛型数组创建的两种技术都有其追随者。第一个更容易读:数组声明为 E[] 类型,这清楚地表明它只包含 E 的实例。它也更简洁:在一个典型的泛型类中,从数组中读取代码中的许多点;第一种技术只需要一次转换(在创建数组的地方),而第二种技术在每次读取数组元素时都需要单独的转换。因此,第一种技术是可取的,在实践中更常用。但是,它确实会造成堆污染([Item-32](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-32-Combine-generics-and-varargs-judiciously.md)):数组的运行时类型与其编译时类型不匹配(除非 E 恰好是 Object)。尽管堆污染在这种情况下是无害的,但这使得一些程序员感到非常不安,因此他们选择了第二种技术。 The following program demonstrates the use of our generic Stack class. The program prints its command line arguments in reverse order and converted to uppercase. No explicit cast is necessary to invoke String’s toUpperCase method on the elements popped from the stack, and the automatically generated cast is guaranteed to succeed: @@ -178,11 +178,11 @@ public static void main(String[] args) { The foregoing(adj. 前述的;前面的;在前的) example may appear to contradict Item 28, which encourages the use of lists in preference to arrays. It is not always possible or desirable to use lists inside your generic types. Java doesn’t support lists natively, so some generic types, such as ArrayList, must be implemented atop arrays. Other generic types, such as HashMap, are implemented atop arrays for performance. The great majority of generic types are like our Stack example in that their type parameters have no restrictions: you can create a Stack, Stack, Stack>, or Stack of any other object reference type. Note that you can’t create a Stack of a primitive type: trying to create a Stack or Stack will result in a compile-time error. -前面的例子可能与 [Item-28](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-28-Prefer-lists-to-arrays.md) 相矛盾,Item-28 鼓励优先使用列表而不是数组。在泛型中使用列表并不总是可能的或可取的。Java 本身不支持列表,因此一些泛型(如 ArrayList)必须在数组之上实现。其他泛型(如 HashMap)是在数组之上实现的,以提高性能。大多数泛型与我们的 Stack 示例相似,因为它们的类型参数没有限制:你可以创建 Stack、Stack、Stack> 或任何其他对象引用类型的堆栈。注意,不能创建基本类型的 Stack:试图创建 Stack 或 Stack 将导致编译时错误。 +前面的例子可能与 [Item-28](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-28-Prefer-lists-to-arrays.md) 相矛盾,Item-28 鼓励优先使用列表而不是数组。在泛型中使用列表并不总是可能的或可取的。Java 本身不支持列表,因此一些泛型(如 ArrayList)必须在数组之上实现。其他泛型(如 HashMap)是在数组之上实现的,以提高性能。大多数泛型与我们的 Stack 示例相似,因为它们的类型参数没有限制:你可以创建 Stack、Stack、Stack> 或任何其他对象引用类型的堆栈。注意,不能创建基本类型的 Stack:试图创建 Stack 或 Stack 将导致编译时错误。 This is a fundamental limitation of Java’s generic type system. You can work around this restriction by using boxed primitive types (Item 61). There are some generic types that restrict the permissible values of their type parameters. For example, consider java.util.concurrent.DelayQueue, whose declaration looks like this: -这是 Java 泛型系统的一个基本限制。你可以通过使用装箱的基本类型([Item-61](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9-Item-61-Prefer-primitive-types-to-boxed-primitives.md))来绕过这一限制。有一些泛型限制了其类型参数的允许值。例如,考虑 java.util.concurrent.DelayQueue,其声明如下: +这是 Java 泛型系统的一个基本限制。你可以通过使用装箱的基本类型([Item-61](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9/Chapter-9-Item-61-Prefer-primitive-types-to-boxed-primitives.md))来绕过这一限制。有一些泛型限制了其类型参数的允许值。例如,考虑 java.util.concurrent.DelayQueue,其声明如下: ``` class DelayQueue implements BlockingQueue @@ -194,4 +194,4 @@ The type parameter list () requires that the actual type para In summary, generic types are safer and easier to use than types that require casts in client code. When you design new types, make sure that they can be used without such casts. This will often mean making the types generic. If you have any existing types that should be generic but aren’t, generify them. This will make life easier for new users of these types without breaking existing clients (Item 26). -总之,泛型比需要在客户端代码中转换的类型更安全、更容易使用。在设计新类型时,请确保可以在不使用此类类型转换的情况下使用它们。这通常意味着使类型具有通用性。如果你有任何应该是泛型但不是泛型的现有类型,请对它们进行泛型。这将使这些类型的新用户在不破坏现有客户端([Item-26](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-26-Do-not-use-raw-types.md))的情况下更容易使用。 +总之,泛型比需要在客户端代码中转换的类型更安全、更容易使用。在设计新类型时,请确保可以在不使用此类类型转换的情况下使用它们。这通常意味着使类型具有通用性。如果你有任何应该是泛型但不是泛型的现有类型,请对它们进行泛型。这将使这些类型的新用户在不破坏现有客户端([Item-26](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-26-Do-not-use-raw-types.md))的情况下更容易使用。 diff --git a/Chapter-5-Item-30-Favor-generic-methods.md b/Chapter-5/Chapter-5-Item-30-Favor-generic-methods.md similarity index 85% rename from Chapter-5-Item-30-Favor-generic-methods.md rename to Chapter-5/Chapter-5-Item-30-Favor-generic-methods.md index 9f8f9e2..209f1e4 100644 --- a/Chapter-5-Item-30-Favor-generic-methods.md +++ b/Chapter-5/Chapter-5-Item-30-Favor-generic-methods.md @@ -38,7 +38,7 @@ addAll(Collection) as a member of raw type Set To fix these warnings and make the method typesafe, modify its declaration to declare a type parameter representing the element type for the three sets (the two arguments and the return value) and use this type parameter throughout the method. **The type parameter list, which declares the type parameters, goes between a method’s modifiers and its return type.** In this example, the type parameter list is , and the return type is Set. The naming conventions for type parameters are the same for generic methods and generic types (Items 29, 68): -要修复这些警告并使方法类型安全,请修改其声明,以声明表示三个集合(两个参数和返回值)的元素类型的类型参数,并在整个方法中使用该类型参数。类型参数列表声明类型参数,它位于方法的修饰符与其返回类型之间。在本例中,类型参数列表为 ,返回类型为 Set。类型参数的命名约定与泛型方法和泛型类型的命名约定相同([Item-29](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-29-Favor-generic-types.md)、[Item-68](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9-Item-68-Adhere-to-generally-accepted-naming-conventions.md)): +要修复这些警告并使方法类型安全,请修改其声明,以声明表示三个集合(两个参数和返回值)的元素类型的类型参数,并在整个方法中使用该类型参数。类型参数列表声明类型参数,它位于方法的修饰符与其返回类型之间。在本例中,类型参数列表为 ,返回类型为 Set。类型参数的命名约定与泛型方法和泛型类型的命名约定相同([Item-29](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-29-Favor-generic-types.md)、[Item-68](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9/Chapter-9-Item-68-Adhere-to-generally-accepted-naming-conventions.md)): ``` // Generic method @@ -69,15 +69,15 @@ When you run the program, it prints [Moe, Tom, Harry, Larry, Curly, Dick]. (The A limitation of the union method is that the types of all three sets (both input parameters and the return value) have to be exactly the same. You can make the method more flexible by using bounded wildcard types (Item 31). -union 方法的一个限制是,所有三个集合(输入参数和返回值)的类型必须完全相同。你可以通过使用有界通配符类型([Item-31](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-31-Use-bounded-wildcards-to-increase-API-flexibility.md))使方法更加灵活。 +union 方法的一个限制是,所有三个集合(输入参数和返回值)的类型必须完全相同。你可以通过使用有界通配符类型([Item-31](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-31-Use-bounded-wildcards-to-increase-API-flexibility.md))使方法更加灵活。 On occasion, you will need to create an object that is immutable but applicable to many different types. Because generics are implemented by erasure (Item 28), you can use a single object for all required type parameterizations, but you need to write a static factory method to repeatedly dole out the object for each requested type parameterization. This pattern, called the generic singleton factory, is used for function objects (Item 42) such as Collections.reverseOrder, and occasionally for collections such as Collections.emptySet. -有时,你需要创建一个对象,该对象是不可变的,但适用于许多不同类型。因为泛型是由擦除([Item-28](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-28-Prefer-lists-to-arrays.md))实现的,所以你可以为所有需要的类型参数化使用单个对象,但是你需要编写一个静态工厂方法,为每个请求的类型参数化重复分配对象。这种模式称为泛型单例工厂,可用于函数对象([Item-42](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-7-Item-42-Prefer-lambdas-to-anonymous-classes.md)),如 Collections.reverseOrder,偶尔也用于集合,如 Collections.emptySet。 +有时,你需要创建一个对象,该对象是不可变的,但适用于许多不同类型。因为泛型是由擦除([Item-28](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-28-Prefer-lists-to-arrays.md))实现的,所以你可以为所有需要的类型参数化使用单个对象,但是你需要编写一个静态工厂方法,为每个请求的类型参数化重复分配对象。这种模式称为泛型单例工厂,可用于函数对象([Item-42](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-7/Chapter-7-Item-42-Prefer-lambdas-to-anonymous-classes.md)),如 Collections.reverseOrder,偶尔也用于集合,如 Collections.emptySet。 Suppose that you want to write an identity function dispenser. The libraries provide Function.identity, so there’s no reason to write your own (Item 59), but it is instructive. It would be wasteful to create a new identity function object time one is requested, because it’s stateless. If Java’s generics were reified, you would need one identity function per type, but since they’re erased a generic singleton will suffice. Here’s how it looks: -假设你想要编写一个恒等函数分发器。这些库提供 Function.identity,所以没有理由编写自己的库([Item-59](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9-Item-59-Know-and-use-the-libraries.md)),但是它很有指导意义。在请求标识函数对象时创建一个新的标识函数对象是浪费时间的,因为它是无状态的。如果 Java 的泛型被具体化了,那么每个类型都需要一个标识函数,但是由于它们已经被擦除,一个泛型单例就足够了。它是这样的: +假设你想要编写一个恒等函数分发器。这些库提供 Function.identity,所以没有理由编写自己的库([Item-59](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-9/Chapter-9-Item-59-Know-and-use-the-libraries.md)),但是它很有指导意义。在请求标识函数对象时创建一个新的标识函数对象是浪费时间的,因为它是无状态的。如果 Java 的泛型被具体化了,那么每个类型都需要一个标识函数,但是由于它们已经被擦除,一个泛型单例就足够了。它是这样的: ``` // Generic singleton factory pattern @@ -116,7 +116,7 @@ public static void main(String[] args) { It is permissible, though relatively rare, for a type parameter to be bounded by some expression involving that type parameter itself. This is what’s known as a recursive type bound. A common use of recursive type bounds is in connection with the Comparable interface, which defines a type’s natural ordering (Item 14). This interface is shown here: -允许类型参数被包含该类型参数本身的表达式限制,尽管这种情况比较少见。这就是所谓的递归类型限定。递归类型边界的一个常见用法是与 Comparable 接口相关联,后者定义了类型的自然顺序([Item-14](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3-Item-14-Consider-implementing-Comparable.md))。该界面如下图所示: +允许类型参数被包含该类型参数本身的表达式限制,尽管这种情况比较少见。这就是所谓的递归类型限定。递归类型边界的一个常见用法是与 Comparable 接口相关联,后者定义了类型的自然顺序([Item-14](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3/Chapter-3-Item-14-Consider-implementing-Comparable.md))。该界面如下图所示: ``` public interface Comparable { @@ -163,12 +163,12 @@ public static > E max(Collection c) { Note that this method throws IllegalArgumentException if the list is empty. A better alternative would be to return an Optional (Item 55). -注意,如果列表为空,该方法将抛出 IllegalArgumentException 异常。更好的选择是返回一个 Optional([Item-55](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-8-Item-55-Return-optionals-judiciously.md))。 +注意,如果列表为空,该方法将抛出 IllegalArgumentException 异常。更好的选择是返回一个 Optional([Item-55](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-8/Chapter-8-Item-55-Return-optionals-judiciously.md))。 Recursive type bounds can get much more complex, but luckily they rarely do. If you understand this idiom, its wildcard variant (Item 31), and the simulated self-type idiom (Item 2), you’ll be able to deal with most of the recursive type bounds you encounter in practice. -递归类型限定可能会变得复杂得多,但幸运的是,这种情况很少。如果你理解这个习惯用法、它的通配符变量([Item-31](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-31-Use-bounded-wildcards-to-increase-API-flexibility.md))和模拟的自类型习惯用法([Item-2](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2-Item-2-Consider-a-builder-when-faced-with-many-constructor-parameters.md)),你就能够处理在实践中遇到的大多数递归类型限定。 +递归类型限定可能会变得复杂得多,但幸运的是,这种情况很少。如果你理解这个习惯用法、它的通配符变量([Item-31](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-31-Use-bounded-wildcards-to-increase-API-flexibility.md))和模拟的自类型习惯用法([Item-2](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2/Chapter-2-Item-2-Consider-a-builder-when-faced-with-many-constructor-parameters.md)),你就能够处理在实践中遇到的大多数递归类型限定。 In summary, generic methods, like generic types, are safer and easier to use than methods requiring their clients to put explicit casts on input parameters and return values. Like types, you should make sure that your methods can be used without casts, which often means making them generic. And like types, you should generify existing methods whose use requires casts. This makes life easier for new users without breaking existing clients (Item 26). -总之,与要求客户端对输入参数和返回值进行显式转换的方法相比,泛型方法与泛型一样,更安全、更容易使用。与类型一样,你应该确保你的方法可以在不使用类型转换的情况下使用,这通常意味着要使它们具有通用性。与类型类似,你应该将需要强制类型转换的现有方法泛型化。这使得新用户在不破坏现有客户端的情况下更容易使用([Item-26](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-26-Do-not-use-raw-types.md))。 +总之,与要求客户端对输入参数和返回值进行显式转换的方法相比,泛型方法与泛型一样,更安全、更容易使用。与类型一样,你应该确保你的方法可以在不使用类型转换的情况下使用,这通常意味着要使它们具有通用性。与类型类似,你应该将需要强制类型转换的现有方法泛型化。这使得新用户在不破坏现有客户端的情况下更容易使用([Item-26](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-26-Do-not-use-raw-types.md))。 diff --git a/Chapter-5-Item-31-Use-bounded-wildcards-to-increase-API-flexibility.md b/Chapter-5/Chapter-5-Item-31-Use-bounded-wildcards-to-increase-API-flexibility.md similarity index 94% rename from Chapter-5-Item-31-Use-bounded-wildcards-to-increase-API-flexibility.md rename to Chapter-5/Chapter-5-Item-31-Use-bounded-wildcards-to-increase-API-flexibility.md index 8ee0589..a6452c1 100644 --- a/Chapter-5-Item-31-Use-bounded-wildcards-to-increase-API-flexibility.md +++ b/Chapter-5/Chapter-5-Item-31-Use-bounded-wildcards-to-increase-API-flexibility.md @@ -4,13 +4,13 @@ As noted in Item 28, parameterized types are invariant. In other words, for any two distinct types `Type1` and `Type2`, `List` is neither a subtype nor a supertype of `List`. Although it is counterintuitive that `List` is not a subtype of `List`, it really does make sense. You can put any object into a `List`, but you can put only strings into a `List`. Since a `List` can’t do everything a `List` can, it isn’t a subtype (by the Liskov substitution principal, Item 10). -如 [Item-28](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-28-Prefer-lists-to-arrays.md) 所示,参数化类型是不可变的。换句话说,对于任意两种不同类型 `Type1` 和 `Type2`,`List` 既不是 `List` 的子类型,也不是它的父类。虽然 `List` 不是 `List` 的子类型,这和习惯的直觉不符,但它确实有意义。你可以将任何对象放入 `List`,但只能将字符串放入 `List`。因为 `List` 不能做 `List` 能做的所有事情,所以它不是子类型(可通过 Liskov 替换原则来理解这一点,[Item-10](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3-Item-10-Obey-the-general-contract-when-overriding-equals.md))。 +如 [Item-28](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-28-Prefer-lists-to-arrays.md) 所示,参数化类型是不可变的。换句话说,对于任意两种不同类型 `Type1` 和 `Type2`,`List` 既不是 `List` 的子类型,也不是它的父类。虽然 `List` 不是 `List` 的子类型,这和习惯的直觉不符,但它确实有意义。你可以将任何对象放入 `List`,但只能将字符串放入 `List`。因为 `List` 不能做 `List` 能做的所有事情,所以它不是子类型(可通过 Liskov 替换原则来理解这一点,[Item-10](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3/Chapter-3-Item-10-Obey-the-general-contract-when-overriding-equals.md))。 ***译注:里氏替换原则(Liskov Substitution Principle,LSP)面向对象设计的基本原则之一。里氏替换原则指出:任何父类可以出现的地方,子类一定可以出现。LSP是继承复用的基石,只有当衍生类可以替换掉父类,软件单位的功能不受到影响时,父类才能真正被复用,而衍生类也能够在父类的基础上增加新的行为。*** Sometimes you need more flexibility than invariant typing can provide. Consider the Stack class from Item 29. To refresh your memory, here is its public API: -有时你需要获得比不可变类型更多的灵活性。考虑 [Item-29](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-29-Favor-generic-types.md) 中的堆栈类。以下是它的公共 API: +有时你需要获得比不可变类型更多的灵活性。考虑 [Item-29](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-29-Favor-generic-types.md) 中的堆栈类。以下是它的公共 API: ``` public class Stack { @@ -118,7 +118,7 @@ In other words, if a parameterized type represents a T producer, use ` choices) @@ -139,7 +139,7 @@ And would this change make any difference in practice? Yes, it would. Suppose yo Now let’s look at the union method from Item 30. Here is the declaration: -现在让我们看看 [Item-30](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-30-Favor-generic-methods.md) 中的 union 方法。以下是声明: +现在让我们看看 [Item-30](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-30-Favor-generic-methods.md) 中的 union 方法。以下是声明: ``` public static Set union(Set s1, Set s2) @@ -192,7 +192,7 @@ Set numbers = Union.union(integers, doubles); Next let’s turn our attention to the max method in Item 30. Here is the original declaration: -接下来让我们将注意力转到 [Item-30](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-30-Favor-generic-methods.md) 中的 max 方法。以下是原始声明: +接下来让我们将注意力转到 [Item-30](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-30-Favor-generic-methods.md) 中的 max 方法。以下是原始声明: ``` public static > T max(List list) @@ -224,7 +224,7 @@ The reason that you can’t apply the original method declaration to this list i There is one more wildcard-related topic that bears discussing. There is a duality(n. 二元性) between type parameters and wildcards, and many methods can be declared using one or the other. For example, here are two possible declarations for a static method to swap two indexed items in a list. The first uses an unbounded type parameter (Item 30) and the second an unbounded wildcard: -还有一个与通配符相关的主题值得讨论。类型参数和通配符之间存在对偶性,可以使用其中一种方法声明许多方法。例如,下面是静态方法的两种可能声明,用于交换列表中的两个索引项。第一个使用无界类型参数([Item-30](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-30-Favor-generic-methods.md)),第二个使用无界通配符: +还有一个与通配符相关的主题值得讨论。类型参数和通配符之间存在对偶性,可以使用其中一种方法声明许多方法。例如,下面是静态方法的两种可能声明,用于交换列表中的两个索引项。第一个使用无界类型参数([Item-30](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-30-Favor-generic-methods.md)),第二个使用无界通配符: ``` // Two possible declarations for the swap method diff --git a/Chapter-5-Item-32-Combine-generics-and-varargs-judiciously.md b/Chapter-5/Chapter-5-Item-32-Combine-generics-and-varargs-judiciously.md similarity index 91% rename from Chapter-5-Item-32-Combine-generics-and-varargs-judiciously.md rename to Chapter-5/Chapter-5-Item-32-Combine-generics-and-varargs-judiciously.md index 4517868..cd66c83 100644 --- a/Chapter-5-Item-32-Combine-generics-and-varargs-judiciously.md +++ b/Chapter-5/Chapter-5-Item-32-Combine-generics-and-varargs-judiciously.md @@ -4,11 +4,11 @@ Varargs methods (Item 53) and generics were both added to the platform in Java 5, so you might expect them to interact gracefully; sadly, they do not. The purpose of varargs is to allow clients to pass a variable number of arguments to a method, but it is a leaky(adj. 漏的;有漏洞的) abstraction: when you invoke a varargs method, an array is created to hold the varargs parameters; that array, which should be an implementation detail, is visible. As a consequence, you get confusing compiler warnings when varargs parameters have generic or parameterized types. -可变参数方法([Item-53](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-8-Item-53-Use-varargs-judiciously.md))和泛型都是在 Java 5 中添加的,因此你可能认为它们能够优雅地交互;可悲的是,他们并不能。可变参数的目的是允许客户端向方法传递可变数量的参数,但这是一个漏洞百出的抽象概念:当你调用可变参数方法时,将创建一个数组来保存参数;该数组的实现细节应该是可见的。因此,当可变参数具有泛型或参数化类型时,会出现令人困惑的编译器警告。 +可变参数方法([Item-53](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-8/Chapter-8-Item-53-Use-varargs-judiciously.md))和泛型都是在 Java 5 中添加的,因此你可能认为它们能够优雅地交互;可悲的是,他们并不能。可变参数的目的是允许客户端向方法传递可变数量的参数,但这是一个漏洞百出的抽象概念:当你调用可变参数方法时,将创建一个数组来保存参数;该数组的实现细节应该是可见的。因此,当可变参数具有泛型或参数化类型时,会出现令人困惑的编译器警告。 Recall from Item 28 that a non-reifiable type is one whose runtime representation(n. 代表;表现;表示法;陈述) has less information than its compile-time representation, and that nearly all generic and parameterized types are non-reifiable. If a method declares its varargs parameter to be of a non-reifiable type, the compiler generates a warning on the declaration. If the method is invoked on varargs parameters whose inferred(adj. 推论的;推测出的) type is non-reifiable, the compiler generates a warning on the invocation too. The warnings look something like this: -回想一下 [Item-28](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-28-Prefer-lists-to-arrays.md),非具体化类型是指其运行时表示的信息少于其编译时表示的信息,并且几乎所有泛型和参数化类型都是不可具体化的。如果方法声明其可变参数为不可具体化类型,编译器将在声明上生成警告。如果方法是在其推断类型不可具体化的可变参数上调用的,编译器也会在调用时生成警告。生成的警告就像这样: +回想一下 [Item-28](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-28-Prefer-lists-to-arrays.md),非具体化类型是指其运行时表示的信息少于其编译时表示的信息,并且几乎所有泛型和参数化类型都是不可具体化的。如果方法声明其可变参数为不可具体化类型,编译器将在声明上生成警告。如果方法是在其推断类型不可具体化的可变参数上调用的,编译器也会在调用时生成警告。生成的警告就像这样: ``` warning: [unchecked] Possible heap pollution from parameterized vararg type List @@ -20,7 +20,7 @@ Heap pollution occurs when a variable of a parameterized type refers to an objec For example, consider this method, which is a thinly disguised(伪装的) variant of the code fragment(n. 碎片;片段或不完整部分) on page 127: -例如,考虑这个方法,它摘自 127 页([Item-26](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-26-Do-not-use-raw-types.md))的代码片段,但做了些修改: +例如,考虑这个方法,它摘自 127 页([Item-26](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-26-Do-not-use-raw-types.md))的代码片段,但做了些修改: ``` // Mixing generics and varargs can violate type safety! @@ -43,7 +43,7 @@ This example raises an interesting question: Why is it even legal to declare a m Prior to Java 7, there was nothing the author of a method with a generic varargs parameter could do about the warnings at the call sites. This made these APIs unpleasant to use. Users had to put up with the warnings or, preferably, to eliminate them with @SuppressWarnings("unchecked") annotations at every call site (Item 27). This was tedious, harmed readability, and hid warnings that flagged real issues. -在 Java 7 之前,使用泛型可变参数的方法的作者对调用点上产生的警告无能为力。使得这些 API 难以使用。用户必须忍受这些警告,或者在每个调用点([Item-27](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-27-Eliminate-unchecked-warnings.md))使用 @SuppressWarnings("unchecked") 注释消除这些警告。这种做法乏善可陈,既损害了可读性,也忽略了标记实际问题的警告。 +在 Java 7 之前,使用泛型可变参数的方法的作者对调用点上产生的警告无能为力。使得这些 API 难以使用。用户必须忍受这些警告,或者在每个调用点([Item-27](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-27-Eliminate-unchecked-warnings.md))使用 @SuppressWarnings("unchecked") 注释消除这些警告。这种做法乏善可陈,既损害了可读性,也忽略了标记实际问题的警告。 In Java 7, the SafeVarargs annotation was added to the platform, to allow the author of a method with a generic varargs parameter to suppress client warnings automatically. In essence, **the SafeVarargs annotation constitutes a promise by the author of a method that it is typesafe.** In exchange for this promise, the compiler agrees not to warn the users of the method that calls may be unsafe. @@ -143,7 +143,7 @@ Note that the SafeVarargs annotation is legal only on methods that can’t be ov An alternative to using the SafeVarargs annotation is to take the advice of Item 28 and replace the varargs parameter (which is an array in disguise) with a List parameter. Here’s how this approach looks when applied to our flatten method. Note that only the parameter declaration has changed: -使用 SafeVarargs 注释的另一种选择是接受 [Item-28](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-28-Prefer-lists-to-arrays.md) 的建议,并用 List 参数替换可变参数(它是一个伪装的数组)。下面是将这种方法应用到我们的 flatten 方法时的效果。注意,只有参数声明发生了更改: +使用 SafeVarargs 注释的另一种选择是接受 [Item-28](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-28-Prefer-lists-to-arrays.md) 的建议,并用 List 参数替换可变参数(它是一个伪装的数组)。下面是将这种方法应用到我们的 flatten 方法时的效果。注意,只有参数声明发生了更改: ``` // List as a typesafe alternative to a generic varargs parameter diff --git a/Chapter-5-Item-33-Consider-typesafe-heterogeneous-containers.md b/Chapter-5/Chapter-5-Item-33-Consider-typesafe-heterogeneous-containers.md similarity index 90% rename from Chapter-5-Item-33-Consider-typesafe-heterogeneous-containers.md rename to Chapter-5/Chapter-5-Item-33-Consider-typesafe-heterogeneous-containers.md index 2db35e9..73e2617 100644 --- a/Chapter-5-Item-33-Consider-typesafe-heterogeneous-containers.md +++ b/Chapter-5/Chapter-5-Item-33-Consider-typesafe-heterogeneous-containers.md @@ -109,7 +109,7 @@ This is precisely what’s needed by the getFavorite method. It is what allows u There are two limitations to the Favorites class that are worth noting. First, a malicious client could easily corrupt the type safety of a Favorites instance, by using a Class object in its raw form. But the resulting client code would generate an unchecked warning when it was compiled. This is no different from a normal collection implementations such as HashSet and HashMap. You can easily put a String into a `HashSet` by using the raw type HashSet (Item 26). That said, you can have runtime type safety if you’re willing to pay for it. The way to ensure that Favorites never violates its type invariant is to have the putFavorite method check that instance is actually an instance of the type represented by type, and we already know how to do this. Just use a dynamic cast: -Favorites 类有两个`值`得注意的限制。首先,恶意客户端很容易通过使用原始形式的类对象破坏 Favorites 实例的类型安全。但是生成的客户端代码在编译时将生成一个 unchecked 警告。这与普通的集合实现(如 HashSet 和 HashMap)没有什么不同。通过使用原始类型 HashSet([Item-26](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-26-Do-not-use-raw-types.md)),可以轻松地将 String 类型放入 `HashSet` 中。也就是说,如果你愿意付出代价的话,你可以拥有运行时类型安全。确保 Favorites 不会违反其类型不变量的方法是让 putFavorite 方法检查实例是否是 type 表示的类型的实例,我们已经知道如何做到这一点。只需使用动态转换: +Favorites 类有两个`值`得注意的限制。首先,恶意客户端很容易通过使用原始形式的类对象破坏 Favorites 实例的类型安全。但是生成的客户端代码在编译时将生成一个 unchecked 警告。这与普通的集合实现(如 HashSet 和 HashMap)没有什么不同。通过使用原始类型 HashSet([Item-26](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-26-Do-not-use-raw-types.md)),可以轻松地将 String 类型放入 `HashSet` 中。也就是说,如果你愿意付出代价的话,你可以拥有运行时类型安全。确保 Favorites 不会违反其类型不变量的方法是让 putFavorite 方法检查实例是否是 type 表示的类型的实例,我们已经知道如何做到这一点。只需使用动态转换: ``` // Achieving runtime type safety with a dynamic cast @@ -124,15 +124,15 @@ java.util.Collections 中的集合包装器也具有相同的功能。它们被 The second limitation of the Favorites class is that it cannot be used on a non-reifiable type (Item 28). In other words, you can store your favorite String or String[], but not your favorite `List`. If you try to store your favorite `List`, your program won’t compile. The reason is that you can’t get a Class object for `List`. The class literal `List.class` is a syntax error, and it’s a good thing, too. `List` and `List` share a single Class object, which is List.class. It would wreak havoc with the internals of a Favorites object if the “type literals” `List.class` and `List.class` were legal and returned the same object reference. There is no entirely satisfactory workaround for this limitation. -Favorites 类的第二个限制是它不能用于不可具体化的类型([Item-28](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-28-Prefer-lists-to-arrays.md))。换句话说,你可以存储的 Favorites 实例类型为 String 类型或 String[],但不能存储 `List`。原因是你不能为 `List` 获取 Class 对象,`List.class` 是一个语法错误,这也是一件好事。`List` 和 `List` 共享一个 Class 对象,即 List.class。如果「字面类型」`List.class` 和 `List.class` 是合法的,并且返回相同的对象引用,那么它将严重破坏 Favorites 对象的内部结构。对于这个限制,没有完全令人满意的解决方案。 +Favorites 类的第二个限制是它不能用于不可具体化的类型([Item-28](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-28-Prefer-lists-to-arrays.md))。换句话说,你可以存储的 Favorites 实例类型为 String 类型或 String[],但不能存储 `List`。原因是你不能为 `List` 获取 Class 对象,`List.class` 是一个语法错误,这也是一件好事。`List` 和 `List` 共享一个 Class 对象,即 List.class。如果「字面类型」`List.class` 和 `List.class` 是合法的,并且返回相同的对象引用,那么它将严重破坏 Favorites 对象的内部结构。对于这个限制,没有完全令人满意的解决方案。 The type tokens used by Favorites are unbounded: getFavorite and put-Favorite accept any Class object. Sometimes you may need to limit the types that can be passed to a method. This can be achieved with a bounded type token, which is simply a type token that places a bound on what type can be represented, using a bounded type parameter (Item 30) or a bounded wildcard (Item 31). -Favorites 使用的类型标记是无界的:getFavorite 和 put-Favorite 接受任何 Class 对象。有时你可能需要限制可以传递给方法的类型。这可以通过有界类型标记来实现,它只是一个类型标记,使用有界类型参数([Item-30](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-30-Favor-generic-methods.md))或有界通配符([Item-31](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-31-Use-bounded-wildcards-to-increase-API-flexibility.md))对可以表示的类型进行绑定。 +Favorites 使用的类型标记是无界的:getFavorite 和 put-Favorite 接受任何 Class 对象。有时你可能需要限制可以传递给方法的类型。这可以通过有界类型标记来实现,它只是一个类型标记,使用有界类型参数([Item-30](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-30-Favor-generic-methods.md))或有界通配符([Item-31](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-31-Use-bounded-wildcards-to-increase-API-flexibility.md))对可以表示的类型进行绑定。 The annotations API (Item 39) makes extensive use of bounded type tokens. For example, here is the method to read an annotation at runtime. This method comes from the AnnotatedElement interface, which is implemented by the reflective types that represent classes, methods, fields, and other program elements: -annotation API([Item-39](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-6-Item-39-Prefer-annotations-to-naming-patterns.md))广泛使用了有界类型标记。例如,下面是在运行时读取注释的方法。这个方法来自 AnnotatedElement 接口,它是由表示类、方法、字段和其他程序元素的反射类型实现的: +annotation API([Item-39](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-6/Chapter-6-Item-39-Prefer-annotations-to-naming-patterns.md))广泛使用了有界类型标记。例如,下面是在运行时读取注释的方法。这个方法来自 AnnotatedElement 接口,它是由表示类、方法、字段和其他程序元素的反射类型实现的: ``` public @@ -145,7 +145,7 @@ The argument, annotationType, is a bounded type token representing an annotation Suppose you have an object of type `Class` and you want to pass it to a method that requires a bounded type token, such as getAnnotation. You could cast the object to `Class`, but this cast is unchecked, so it would generate a compile-time warning (Item 27). Luckily, class Class provides an instance method that performs this sort of cast safely (and dynamically). The method is called asSubclass, and it casts the Class object on which it is called to represent a subclass of the class represented by its argument. If the cast succeeds, the method returns its argument; if it fails, it throws a ClassCastException. -假设你有一个 `Class` 类型的对象,并且希望将其传递给一个需要有界类型令牌(例如 getAnnotation)的方法。你可以将对象强制转换为 `Class`,但是这个强制转换是未选中的,因此它将生成一个编译时警告([Item-27](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5-Item-27-Eliminate-unchecked-warnings.md))。幸运的是,class 类提供了一个实例方法,可以安全地(动态地)执行这种类型的强制转换。该方法称为 asSubclass,它将类对象强制转换为它所调用的类对象,以表示由其参数表示的类的子类。如果转换成功,则该方法返回其参数;如果失败,则抛出 ClassCastException。 +假设你有一个 `Class` 类型的对象,并且希望将其传递给一个需要有界类型令牌(例如 getAnnotation)的方法。你可以将对象强制转换为 `Class`,但是这个强制转换是未选中的,因此它将生成一个编译时警告([Item-27](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-5/Chapter-5-Item-27-Eliminate-unchecked-warnings.md))。幸运的是,class 类提供了一个实例方法,可以安全地(动态地)执行这种类型的强制转换。该方法称为 asSubclass,它将类对象强制转换为它所调用的类对象,以表示由其参数表示的类的子类。如果转换成功,则该方法返回其参数;如果失败,则抛出 ClassCastException。 Here’s how you use the asSubclass method to read an annotation whose type is unknown at compile time. This method compiles without error or warning: diff --git a/Chapter-6-Introduction.md b/Chapter-6/Chapter-6-Introduction.md similarity index 100% rename from Chapter-6-Introduction.md rename to Chapter-6/Chapter-6-Introduction.md diff --git a/Chapter-6-Item-34-Use-enums-instead-of-int-constants.md b/Chapter-6/Chapter-6-Item-34-Use-enums-instead-of-int-constants.md similarity index 94% rename from Chapter-6-Item-34-Use-enums-instead-of-int-constants.md rename to Chapter-6/Chapter-6-Item-34-Use-enums-instead-of-int-constants.md index ee47f27..543fca9 100644 --- a/Chapter-6-Item-34-Use-enums-instead-of-int-constants.md +++ b/Chapter-6/Chapter-6-Item-34-Use-enums-instead-of-int-constants.md @@ -56,7 +56,7 @@ On the surface, these enum types may appear similar to those of other languages, The basic idea behind Java’s enum types is simple: they are classes that export one instance for each enumeration constant via a public static final field. Enum types are effectively final, by virtue of having no accessible constructors. Because clients can neither create instances of an enum type nor extend it, there can be no instances but the declared enum constants. In other words, enum types are instance-controlled (page 6). They are a generalization of singletons (Item 3), which are essentially single-element enums. -Java 枚举类型背后的基本思想很简单:它们是通过 public static final 修饰的字段为每个枚举常量导出一个实例的类。枚举类型实际上是 final 类型,因为没有可访问的构造函数。客户端既不能创建枚举类型的实例,也不能扩展它,所以除了声明的枚举常量之外,不能有任何实例。换句话说,枚举类型是实例受控的类(参阅第 6 页,[Item-1](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2-Item-1-Consider-static-factory-methods-instead-of-constructors.md))。它们是单例([Item-3](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2-Item-3-Enforce-the-singleton-property-with-a-private-constructor-or-an-enum-type.md))的推广应用,单例本质上是单元素的枚举。 +Java 枚举类型背后的基本思想很简单:它们是通过 public static final 修饰的字段为每个枚举常量导出一个实例的类。枚举类型实际上是 final 类型,因为没有可访问的构造函数。客户端既不能创建枚举类型的实例,也不能扩展它,所以除了声明的枚举常量之外,不能有任何实例。换句话说,枚举类型是实例受控的类(参阅第 6 页,[Item-1](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2/Chapter-2-Item-1-Consider-static-factory-methods-instead-of-constructors.md))。它们是单例([Item-3](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-2/Chapter-2-Item-3-Enforce-the-singleton-property-with-a-private-constructor-or-an-enum-type.md))的推广应用,单例本质上是单元素的枚举。 Enums provide compile-time type safety. If you declare a parameter to be of type Apple, you are guaranteed that any non-null object reference passed to the parameter is one of the three valid Apple values. Attempts to pass values of the wrong type will result in compile-time errors, as will attempts to assign an expression of one enum type to a variable of another, or to use the == operator to compare values of different enum types. @@ -68,7 +68,7 @@ Enum types with identically named constants coexist(vi. 共存;和平共处 In addition to rectifying the deficiencies of int enums, enum types let you add arbitrary methods and fields and implement arbitrary interfaces. They provide high-quality implementations of all the Object methods (Chapter 3), they implement Comparable (Item 14) and Serializable (Chapter 12), and their serialized form is designed to withstand most changes to the enum type. -除了纠正 int 枚举的不足之外,枚举类型还允许添加任意方法和字段并实现任意接口。它们提供了所有 Object 方法的高质量实现(参阅 Chapter 3),还实现了 Comparable([Item-14](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3-Item-14-Consider-implementing-Comparable.md))和 Serializable(参阅 Chapter 12),并且它们的序列化形式被设计成能够适应枚举类型的可变性。 +除了纠正 int 枚举的不足之外,枚举类型还允许添加任意方法和字段并实现任意接口。它们提供了所有 Object 方法的高质量实现(参阅 Chapter 3),还实现了 Comparable([Item-14](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-3/Chapter-3-Item-14-Consider-implementing-Comparable.md))和 Serializable(参阅 Chapter 12),并且它们的序列化形式被设计成能够适应枚举类型的可变性。 So why would you want to add methods or fields to an enum type? For starters, you might want to associate data with its constants. Our Apple and Orange types, for example, might benefit from a method that returns the color of the fruit, or one that returns an image of it. You can augment an enum type with any method that seems appropriate. An enum type can start life as a simple collection of enum constants and evolve over time into a full-featured abstraction. @@ -116,7 +116,7 @@ public enum Planet { It is easy to write a rich enum type such as Planet. **To associate data with enum constants, declare instance fields and write a constructor that takes the data and stores it in the fields.** Enums are by their nature immutable, so all fields should be final (Item 17). Fields can be public, but it is better to make them private and provide public accessors (Item 16). In the case of Planet, the constructor also computes and stores the surface gravity, but this is just an optimization. The gravity could be recomputed from the mass and radius each time it was used by the surfaceWeight method, which takes an object’s mass and returns its weight on the planet represented by the constant. While the Planet enum is simple, it is surprisingly powerful. Here is a short program that takes the earth weight of an object (in any unit) and prints a nice table of the object’s weight on all eight planets (in the same unit): -编写一个富枚举类型很容易,如上述的 Planet。**要将数据与枚举常量关联,可声明实例字段并编写一个构造函数,该构造函数接受数据并将其存储在字段中。** 枚举本质上是不可变的,因此所有字段都应该是 final([Item-17](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-17-Minimize-mutability.md))。字段可以是公共的,但是最好将它们设置为私有并提供公共访问器([Item-16](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-16-In-public-classes-use-accessor-methods-not-public-fields.md))。在 Planet 的例子中,构造函数还计算和存储表面重力,但这只是一个优化。每一次使用 surfaceWeight 方法时,都可以通过质量和半径重新计算重力。surfaceWeight 方法获取一个物体的质量,并返回其在该常数所表示的行星上的重量。虽然 Planet 枚举很简单,但它的力量惊人。下面是一个简短的程序,它获取一个物体的地球重量(以任何单位表示),并打印一个漂亮的表格,显示该物体在所有 8 个行星上的重量(以相同的单位表示): +编写一个富枚举类型很容易,如上述的 Planet。**要将数据与枚举常量关联,可声明实例字段并编写一个构造函数,该构造函数接受数据并将其存储在字段中。** 枚举本质上是不可变的,因此所有字段都应该是 final([Item-17](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-17-Minimize-mutability.md))。字段可以是公共的,但是最好将它们设置为私有并提供公共访问器([Item-16](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-16-In-public-classes-use-accessor-methods-not-public-fields.md))。在 Planet 的例子中,构造函数还计算和存储表面重力,但这只是一个优化。每一次使用 surfaceWeight 方法时,都可以通过质量和半径重新计算重力。surfaceWeight 方法获取一个物体的质量,并返回其在该常数所表示的行星上的重量。虽然 Planet 枚举很简单,但它的力量惊人。下面是一个简短的程序,它获取一个物体的地球重量(以任何单位表示),并打印一个漂亮的表格,显示该物体在所有 8 个行星上的重量(以相同的单位表示): ``` public class WeightTable { @@ -150,7 +150,7 @@ Until 2006, two years after enums were added to Java, Pluto was a planet. This r Some behaviors associated with enum constants may need to be used only from within the class or package in which the enum is defined. Such behaviors are best implemented as private or package-private methods. Each constant then carries with it a hidden collection of behaviors that allows the class or package containing the enum to react appropriately when presented with the constant. Just as with other classes, unless you have a compelling reason to expose an enum method to its clients, declare it private or, if need be, package-private (Item 15). -与枚举常量相关的一些行为可能只需要在定义枚举的类或包中使用。此类行为最好以私有或包私有方法来实现。然后,每个常量都带有一个隐藏的行为集合,允许包含枚举的类或包在使用该常量时做出适当的反应。与其他类一样,除非你有充分的理由向其客户端公开枚举方法,否则将其声明为私有的,或者在必要时声明为包私有([Item-15](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-15-Minimize-the-accessibility-of-classes-and-members.md))。 +与枚举常量相关的一些行为可能只需要在定义枚举的类或包中使用。此类行为最好以私有或包私有方法来实现。然后,每个常量都带有一个隐藏的行为集合,允许包含枚举的类或包在使用该常量时做出适当的反应。与其他类一样,除非你有充分的理由向其客户端公开枚举方法,否则将其声明为私有的,或者在必要时声明为包私有([Item-15](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-15-Minimize-the-accessibility-of-classes-and-members.md))。 ***译注:Java 中访问级别规则如下:*** - ***类访问级别:public(公共)、无修饰符(package-private,包私有)*** @@ -158,7 +158,7 @@ Some behaviors associated with enum constants may need to be used only from with If an enum is generally useful, it should be a top-level class; if its use is tied to a specific top-level class, it should be a member class of that top-level class (Item 24). For example, the java.math.RoundingMode enum represents a rounding mode for decimal fractions. These rounding modes are used by the BigDecimal class, but they provide a useful abstraction that is not fundamentally tied to BigDecimal. By making RoundingMode a top-level enum, the library designers encourage any programmer who needs rounding modes to reuse this enum, leading to increased consistency across APIs. -通常,如果一个枚举用途广泛,那么它应该是顶级类;如果它被绑定到一个特定的顶级类使用,那么它应该是这个顶级类([Item-24](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4-Item-24-Favor-static-member-classes-over-nonstatic.md))的成员类。例如,java.math.RoundingMode 枚举表示小数部分的舍入模式。BigDecimal 类使用这些四舍五入模式,但是它们提供了一个有用的抽象,这个抽象与 BigDecimal 没有本质上的联系。通过使 RoundingMode 成为顶级枚举,库设计人员支持任何需要舍入模式的程序员重用该枚举,从而提高 API 之间的一致性。 +通常,如果一个枚举用途广泛,那么它应该是顶级类;如果它被绑定到一个特定的顶级类使用,那么它应该是这个顶级类([Item-24](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-4/Chapter-4-Item-24-Favor-static-member-classes-over-nonstatic.md))的成员类。例如,java.math.RoundingMode 枚举表示小数部分的舍入模式。BigDecimal 类使用这些四舍五入模式,但是它们提供了一个有用的抽象,这个抽象与 BigDecimal 没有本质上的联系。通过使 RoundingMode 成为顶级枚举,库设计人员支持任何需要舍入模式的程序员重用该枚举,从而提高 API 之间的一致性。 The techniques demonstrated in the Planet example are sufficient(adj. 足够的;充分的) for most enum types, but sometimes you need more. There is different data associated with each Planet constant, but sometimes you need to associate fundamentally different behavior with each constant. For example, suppose you are writing an enum type to represent the operations on a basic four-function calculator and you want to provide a method to perform the arithmetic operation represented by each constant. One way to achieve this is to switch on the value of the enum: @@ -277,11 +277,11 @@ public static Optional fromString(String symbol) { Note that the Operation constants are put into the stringToEnum map from a static field initialization that runs after the enum constants have been created. The previous code uses a stream (Chapter 7) over the array returned by the values() method; prior to Java 8, we would have created an empty hash map and iterated over the values array inserting the string-to-enum mappings into the map, and you can still do it that way if you prefer. But note that attempting to have each constant put itself into a map from its own constructor does not work. It would cause a compilation error, which is good thing because if it were legal, it would cause a NullPointerException at runtime. Enum constructors aren’t permitted to access the enum’s static fields, with the exception of constant variables (Item 34). This restriction is necessary because static fields have not yet been initialized when enum constructors run. A special case of this restriction is that enum constants cannot access one another from their constructors. -注意,Operation 枚举的常量是从创建枚举常量之后运行的静态字段初始化中放入 stringToEnum 的。上述代码在 values() 方法返回的数组上使用流(参阅第 7 章);在 Java 8 之前,我们将创建一个空 HashMap,并遍历值数组,将自定义字符串与枚举的映射插入到 HashMap 中,如果你愿意,你仍然可以这样做。但是请注意,试图让每个常量通过构造函数将自身放入 HashMap 中是行不通的。它会导致编译错误,这是好事,因为如果合法,它会在运行时导致 NullPointerException。枚举构造函数不允许访问枚举的静态字段,常量变量除外([Item-34](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-6-Item-34-Use-enums-instead-of-int-constants.md))。这个限制是必要的,因为在枚举构造函数运行时静态字段还没有初始化。这种限制的一个特殊情况是枚举常量不能从它们的构造函数中相互访问。 +注意,Operation 枚举的常量是从创建枚举常量之后运行的静态字段初始化中放入 stringToEnum 的。上述代码在 values() 方法返回的数组上使用流(参阅第 7 章);在 Java 8 之前,我们将创建一个空 HashMap,并遍历值数组,将自定义字符串与枚举的映射插入到 HashMap 中,如果你愿意,你仍然可以这样做。但是请注意,试图让每个常量通过构造函数将自身放入 HashMap 中是行不通的。它会导致编译错误,这是好事,因为如果合法,它会在运行时导致 NullPointerException。枚举构造函数不允许访问枚举的静态字段,常量变量除外([Item-34](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-6/Chapter-6-Item-34-Use-enums-instead-of-int-constants.md))。这个限制是必要的,因为在枚举构造函数运行时静态字段还没有初始化。这种限制的一个特殊情况是枚举常量不能从它们的构造函数中相互访问。 Also note that the fromString method returns an Optional. This allows the method to indicate that the string that was passed in does not represent a valid operation, and it forces the client to confront this possibility (Item 55). -还要注意 fromString 方法返回一个 Optional。这允许该方法提示传入的字符串并非有效操作,并强制客户端处理这种可能([Item-55](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-8-Item-55-Return-optionals-judiciously.md))。 +还要注意 fromString 方法返回一个 Optional。这允许该方法提示传入的字符串并非有效操作,并强制客户端处理这种可能([Item-55](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-8/Chapter-8-Item-55-Return-optionals-judiciously.md))。 A disadvantage of constant-specific method implementations is that they make it harder to share code among enum constants. For example, consider an enum representing the days of the week in a payroll package. This enum has a method that calculates a worker’s pay for that day given the worker’s base salary (per hour) and the number of minutes worked on that day. On the five weekdays, any time worked in excess of a normal shift generates overtime pay; on the two weekend days, all work generates overtime pay. With a switch statement, it’s easy to do this calculation by applying multiple case labels to each of two code fragments: diff --git a/Chapter-6-Item-35-Use-instance-fields-instead-of-ordinals.md b/Chapter-6/Chapter-6-Item-35-Use-instance-fields-instead-of-ordinals.md similarity index 100% rename from Chapter-6-Item-35-Use-instance-fields-instead-of-ordinals.md rename to Chapter-6/Chapter-6-Item-35-Use-instance-fields-instead-of-ordinals.md diff --git a/Chapter-6-Item-36-Use-EnumSet-instead-of-bit-fields.md b/Chapter-6/Chapter-6-Item-36-Use-EnumSet-instead-of-bit-fields.md similarity index 91% rename from Chapter-6-Item-36-Use-EnumSet-instead-of-bit-fields.md rename to Chapter-6/Chapter-6-Item-36-Use-EnumSet-instead-of-bit-fields.md index 7e1877a..61c4210 100644 --- a/Chapter-6-Item-36-Use-EnumSet-instead-of-bit-fields.md +++ b/Chapter-6/Chapter-6-Item-36-Use-EnumSet-instead-of-bit-fields.md @@ -4,7 +4,7 @@ If the elements of an enumerated type are used primarily in sets, it is traditional to use the int enum pattern (Item 34), assigning a different power of 2 to each constant: -如果枚举类型的元素主要在 Set 中使用,传统上使用 int 枚举模式([Item-34](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-6-Item-34-Use-enums-instead-of-int-constants.md)),通过不同的 2 平方数为每个常量赋值: +如果枚举类型的元素主要在 Set 中使用,传统上使用 int 枚举模式([Item-34](https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual/blob/master/Chapter-6/Chapter-6-Item-34-Use-enums-instead-of-int-constants.md)),通过不同的 2 平方数为每个常量赋值: ``` // Bit field enumeration constants - OBSOLETE! @@ -57,8 +57,8 @@ text.applyStyles(EnumSet.of(Style.BOLD, Style.ITALIC)); Note that the applyStyles method takes a `Set