Open eziceice opened 5 years ago
Some cases that you may don't need to implement equals():
Each instance of the class is inherently unique.
There is no need for the class to provide a "logical equality" test. For example, you shouldn't compare two Pattern objects are equal or not. If you want to compare the regular expression is same or not then just compare the regular expression instead of comparing the pattern objects.
A superclass has already overridden equals, and the superclass behavior is appropriate for this class.
The class is private or package-private, and you are certain that its equals method will never be invoked.
Generally, the appropriate time to override equals is when we want to compare the values for that class, for example, Integer or String. Not only is overriding the equals method necessary to satisfy programmer expectations, it enables instances to serve as map keys or set elements with predicable, desirable behavior. There are five rules for implementing equals.
Reflexivity: an object must be equal to itself.
Symmetry: any two objects must agree on whether they are equal.
// Broken - violates symmetry!
public final class CaseInsensitiveString {
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
}
CaseInsensitiveString cis = new CaseInsensitiveString("Polish"); String s = "polish"; // cis.equals(s) return true // s.equals(cis) return false - case insensitive
- **Transitivity**: 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. **There is no way to extend an instantiable class and add value component while preserving the equals contract, unless you're wiling to forgo the benefits of object-oriented abstraction.** A good work around here is to use composition over inheritance. **无法在扩展可实例化的类的同时,既增加新的值组件,同时又保留equals的约定.** *Note: In Java, java.sql.Timestamp and java.util.Date violate this feature. You shouldn't use Timestamp and Date at the same collection.*
// Adds a value component without violating the equals contract public class ColorPoint { private final Point point; private final Color color; public ColorPoint(int x, int y, Color color) { point = new Point(x, y); this.color = Objects.requireNonNull(color); } /**
- **Consistency**: Mutable objects can be equal to different objects at different times while immutable objects can't. In addition, **do not write an equals method that depends on unreliable resources**. For example, equals method in java.net.URL's relies on comparison of the IP addresses of the hosts associated with the URLs. This is a wrong behavior in Java library.
- **Non-nullity**: All objects must be unequal to null. *A instanceof B - instance of operator is specified to return
false if its first operand (A) is null.*
Here is the list for a high-quality equals method:
To achieve the best performance in equals method, you should first compare fields that are more likely to differ, less expensive to compare, or ideally, both. Don't compare the fields that are not part of an object's logical state, don't compare derived fields,
Few more important points:
It's easy to write and test equals and hashCode methods by using Google's open source AutoValue framework, which is automatically generates these methods for you, triggered by a single annotation on the class.
import com.google.auto.value.AutoValue;
@AutoValue
public abstract class AutoValueMoney {
public abstract String getCurrency();
public abstract long getAmount();
public static AutoValueMoney create(String currency, long amount) {
return new AutoValue_AutoValueMoney(currency, amount);
}
}
// generated file: contains getter, equals, toString and hashcode. // No setter as it is immutable, try to use builder pattern if you want to initial values.
import javax.annotation.Generated;
@Generated("com.google.auto.value.processor.AutoValueProcessor")
final class AutoValue_AutoValueMoney extends AutoValueMoney {
private final String currency;
private final long amount;
AutoValue_AutoValueMoney(
String currency,
long amount) {
if (currency == null) {
throw new NullPointerException("Null currency");
}
this.currency = currency;
this.amount = amount;
}
@Override
public String getCurrency() {
return currency;
}
@Override
public long getAmount() {
return amount;
}
@Override
public String toString() {
return "AutoValueMoney{"
"}";
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (o instanceof AutoValueMoney) {
AutoValueMoney that = (AutoValueMoney) o;
return (this.currency.equals(that.getCurrency()))
&& (this.amount == that.getAmount());
}
return false;
}
@Override
public int hashCode() {
int h$ = 1;
h$ = 1000003;
h$ ^= currency.hashCode();
h$ = 1000003;
h$ ^= (int) ((amount >>> 32) ^ amount);
return h$;
}
}
## Always override *hashCode* when you override *equals*
- **You must override *hashCode* in every class the overrides *equals*.** Failed to do will violate the general contract for hashCode, and also will cause trouble when you're using collections such as *HashMap* or *HashSet*.
- HashCode contract:
- hashCode method must be return the same value of an object in an execution of an application.
- **(Key)**: **If two object are equal in equals() method, their hashCode must return the same value**. You must exclude any fields are not used in equals comparisons, or you risk violating this provision of the hasCode contract.
- If two object are not equal in equals() method, their hashCode can be same or not same. However, distinct hashCode can improve the performance of hash tables.
- Objects.hash(Object... args) can be used to generate a better hashCode instead of manually creating one. However, this will cause some performance issue as this method contains many boxing and unboxing stuff. It is recommended for use it only when the performance is not critical.
- If the hashCode for a kind of class is used as the hash keys often, you can consider to cache the value in the object itself instead of calculating it every time when you call that method.
Map<PhoneNumber, String> m = new HashMap<>(); m.put(new PhoneNumber(707, 867, 5309), "Jenny"); m.get(new PhoneNumber(707, 867, 5309)) // This will return null / HashCode method is not overridden in PhoneNumber object, which must be implemented first if this object needs be the key of a map. It's better to use immutable object as the key. /
## Always override *toString*
- Providing a good *toString* implementation makes your class much more pleasant to use and makes systems using the class easier to debug.
- When practical, the *toString* method should return all of the interesting information contained in the object.
- Whether or not you decide to specify the format, you should clearly document your intentions.
- Provide programmatic access to the information contained in the value returned by *toString*, otherwise programmers will try to use the value in *toString* , which is a really bad behavior.
## Override *clone* judiciously
- *Clone* method is a protected method in Object class. If a class implements Cloneable, Object's clone method return a field-by-field copy of the object; otherwise it throws CloneNotSupportedException. **In practice, a class implementing Cloneable is expected to provide a properly functioning public clone method**.
- In effect, the **clone** method functions as a constructor; you must ensure that it does no harm to the original object and that it properly establishes invariants on the clone.
- Two types of copy in Apache:
- Deepcopy: 拷贝原object中的所有数据,包括基本类型和object类型.
- Shallowcopy: 拷贝原object中的所有基本类型,以及object类型的引用 - 引用指向的仍然还是原来的object.
- The Clonealbe architecture is incompatible with normal use of final fields referring to mutable objects. If you really want to clone a object, all final modifiers need to be removed.
- As a rule, copy functionality is best provided by constructors or factories. **A notable exception of this rule is arrays, which are best copied with the clone method.**
## Consider implementing *Comparable*
- If you are writing a value class with an obvious natural ordering, such as alphabetical order, numerical order, or chronological order, you should implement the *Comparable* interface.
- A class that violates the *compareTo* contract can break other classes that depend on comparison, including TreeSet, TreeMap and the utitly classes Collections and Arrays.
- Same as equals method, there is no way to extend an instanitable class with a new value component while preserving the compareTo contract, unless you are willing to forgo the benefits of object-oriented abstraction, which is using component instead of inheritance.
- compareTo dosen't have to work across objects of different types: when confronted with objects of different types, compareTo is permitted to throw ClassCastException.
- It is strongly recommended that the compareTo method should generally return the same results as the equals method.
- In Java 7, static compare methods were added to all of Java's boxed primitive classes. Use of the relational operators < and > in compareTo methods is verbose and error-prone and no longer recommended.
// Multiple-field Comparable with primitive fields
public int compareTo(PhoneNumber pn) {
int result = Short.compare(areaCode, pn.areaCode);
if (result == 0) {
result = Short.compare(prefix, pn.prefix);
if (result == 0)
result = Short.compare(lineNum, pn.lineNum);
}
return result;
- Using comparator normally comes with a higher performance cost than comparable.
private static final Comparator
comparingInt((PhoneNumber pn) -> pn.areaCode)
.thenComparingInt(pn -> pn.prefix)
.thenComparingInt(pn -> pn.lineNum);
public int compareTo(PhoneNumber pn) {
return COMPARATOR.compare(this, pn);
}
A well-designed component hides all its implementation details, cleanly separating its API from its implementation. Try to make each class or member as inaccessible as possible.
For top-level classes and interfaces, there are only two possible access levels: package-private and public. 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.
Instance fields of public classes should rarely be public, because classes with public mutable fields are not generally thread-safe. 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.
In order to take advantage of the module accessibility in Java 9, you must group your packages into modules, make all of their dependencies explicit in module declarations, rearrange your source tree, and take special actions to accommodate any access to non-modularized packages from within your modules.
If a class is accessible outside its package, provide accessor methods to preserve the flexibility to change the class's internal representation. Even if a class is package-private or is a private nested class, there is nothing inherently wrong with exposing its data fields.
If the fields are immutable, then it is less harmful to use them without accessor. However, it is still better to provide acccessor.
An immutable class is simply a class whose instances cannot be modified. Example in Java: String, BigInteger and BigDecimal.
There are five rules to make a class immutable
Immutable objects are simple, an immutable object can be in exactly one state, the state in which it was created. Meanwhile, immutable objects are inherently thread-safe, they require no synchronization, and it can be shared freely.
Not only can you share immutable objects, but they can share their internals.
Immutable objects make great building blocks for other objects. For example, by making it as a key in map or set.
Immutable objects provide failure atomicity for free.
The major disadvantage of immutable classes is that they require a separate object for each distinct value. The package-private mutable companion class approach works fine if you accurately predict which complex operations clients will want to perform on your immutable class. If not, then your best bet is to provide a public mutable companion class. For example, String and StringBuilder.
In truth, no method may produce an externally visible change in the object's state. However, some immutable classes have one or more nonfinal fields in which they cache the results of expensive computations the first time they are needed. If the same value is requested again, the cached value is returned, saving the cost of recalculation.
In conclusion, classes should be immutable unless there's a very good reason to make them mutable. If a class cannot be made immutable, limit its mutability as much as possible and declare every field private final unless there's a good reason to do otherwise. Constructors should create fully initialized objects with all of their invariants established.
// An example for disadvantage of inheritance
public class MyCollection { // Version 1.0
public void add(String s) {
// add to inner array
}
}
public class LimitedLengthCollection extends MyCollection { @Override public void add(String s) { if (s.length() == 5) { super.add(s); } } }
public class MyCollection { // Version 2.0 public void add(String s) { // add to inner array }
public void addMany(String[] s) {
// iterate on each element and add it to inner array
}
}
LimitedLengthCollection c = new LimitedLengthCollection(); c.addMany(new String[] {"a", "b", "c"}); // Now you can add any size Strings - incorrect
- Using composition could avoid all of the problems described for inheritance, it creates the new class a private field that references an instance of the existing class.
- Wrapper class means a class contains 'wraps' another class instance. The disadvantages of wrapper class are few.
- It is not suited for use in callback frameworks, wherein objects pass self-references to other objects for subsequent invocations ("callbacks"). This is because the wrapper normally doesn't register in the controller to use callback method or the wrapped class and the wrapper both are registered which cause the duplicate issue or **it can say a wrapped object doesn't know of its wrapper.**
- Inheritance is appropriate only in circumstances where the subclass really is a subtype of the superclass. If not, then use composition instead.
// basic class which we will wrap public class Model{ Controller controller;
Model(Controller controller){
this.controller = controller;
controller.register(this); //Pass SELF reference
}
public void makeChange(){
...
}
}
public class Controller{ private final Model model;
public void register(Model model){
this.model = model;
}
// Here the wrapper just fails to count changes,
// because it does not know about the wrapped object
// references leaked
public void doChanges(){
model.makeChange();
}
}
// wrapper class public class ModelChangesCounter{ private final Model; private int changesMade;
ModelWrapper(Model model){
this.model = model;
}
// The wrapper is intended to count changes,
// but those changes which are invoked from
// Controller are just skipped
public void makeChange(){
model.makeChange();
changesMade++;
}
}
- 继承的时候有可能会打破封装的特性, 比如说上面的LimitedLengthCollection和MyCollection, 通常来说继承API的时候并不需要知道继承的类究竟是如何是实现的, 只需要知道它是如何使用的,然而上面的情况并不是这样, 你需要知道MyCollection的addAll()是如何使用的才能正确的使用子类里的addAll()方法, 这就打破了封装的特性.
## Design and document for inheritance or else prohibit it
- The class must document precisely the effects of overriding any method. In other words, the class must document its self-use of overridable methods. For each public or protected method, the documentation must indicate which overridable methods the method invokes, in what sequence and how the results of each invocation affect subsequent processing.
- More generally, a class must document any circumstances under which it might invoke an overridable method. @implSpec - Implementation Requirements can be used to document the inner workings of the method.
- A class may have to provide hooks into its internal workings in the form of judiciously chosen protected methods or, in rare instances, protected fields.
- The only way to test a class designed for inheritance is to write subclasses.
- Constructors must not invoke overridable methods, because the superclass constructor runs before the subclass constructor, so the overriding method in the subclass will get invoked before the subclass constructor has run. Same thing need to be considered when implementing Cloneable or Seriablizable, becasue clone or readObject method do the same thing as constructor. **Neither clone nor readObject may invoke an overrdable method, directly or indirectly.**
public class Super {
// Broken - constructor invokes an overridable method
public Super() {
overrideMe();
}
public void overrideMe() {
}
}
// Here’s a subclass that overrides the overrideMe method, which is erroneously invoked by Super’s sole constructor:
public final class Sub extends Super {
private final Instant instant; // Blank final, set by constructor
Sub() {
instant = Instant.now();
}
// Overriding method invoked by superclass constructor
@Override
public void overrideMe() {
System.out.println(instant);
}
public static void main(String[] args) {
Sub sub = new Sub();
sub.overrideMe();
// one null - cuz the subclass has not been initialized
// one currenty time
}
}
- In summary, designing a class for inheritance is hard to work. All of its self-use patterns must be documented, and once you've documented them, you must commit to them for the life of the class.
## Prefer interfaces to abstract classes
- A skeletal implementation classes are called AbstractInterface, which is an abstract class implements some interface. (Example in Java - set, abstractSet, map and abstractMap.)
public interface Foo { void bar(); void baz(); }
// Abstract Interface private abstract class AbstractFoo implements Foo { ... }
- 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.
- To summarize, an interface is generally the best way to define a type that permits multiple implementations and if you export a nontrivial interface, you should strongly consider providing a skeletal implementation to go with it.
## Design interfaces for posterity
- The declaration for a default method includes a default implementation that is used by all classes that implement the interface but do not implement the default method. While the addition of default methods to Java makes it possible to add methods to an existing interface, there is no guarantee that these methods will work in all preexisting implementations. Defaults methods are "injected" into existing implementations without the knowledge or consent of their implementors.
## Favor static member classes over nonstatic
- A nested class should exist only to serve its enclosing class. If a nested class would be useful in some other context, then it should be a top-level class. There are four kinds of nested classes: static member classes, nonstatic member classes, anonymous classes, and local classes. **All but the first kind are known as inner classes**.
- A common use of a static member class is as a public helper class, useful only in conjunction with its outer class.
- One common use of a nonstatic member class is to define an Adapter that allows an instance of the outer class to be viewed as an instance of some unrelated class.
- If you declare a member class that does not require access to an enclosing instance, always put the static modifier in its declaration. If you omit this modifier, each instance will have a hidden extraneous reference to its enclosing instance, 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.
- Two common use of anonymous class, small functions of objects (replaced by lambda) and used in implementation of static factory method.
It is legal to use raw types (generic types without their parameters), but you should never do it. If you use raw types, you will lose all the safety and expressiveness benefits of generics.
There are sub-typing rules for generics, and List\
If you use unbounded wildcard (?) in the collection, it is safe. However, it means you can't put any element (other than null) into a Collection<?>.
Generic type information is erased at runtime, it is illegal to use the instanceof operator parameterized types other than unbounded wildcard types.
Using raw types can lead to exceptions at runtime, Collection\
Every time use a @suppressWarnings("unchecked") annotation, add a comment saying why it is safe to do it.
Optional<T>
。抛异常开销稍高,因为创建异常需要抛出整个堆栈的跟踪。而抛出null则让客户端的代码更加复杂。Optional<T>
可以使得代码比抛出异常更灵活更容易使用,而且比返回null更不容易出错。Optional.of
不允许传递null值,Optional.ofNullable
允许传递null。IllegalArgumentException
,当调用者传递的参数值不合适的时候,抛出此类异常。IllegalStateException
,如果因为接受对象的状态而使调用非法,通常会抛出这个异常。UnsupportedOperationException
通常在对象不支持锁请求的操作时抛出。
Chapter 2. Creating and Destorying Objects
Consider static factory methods instead of constructors
Advantage:
Static factory has name which is better for people to understand it compared to constructors. If you want to create two constructors with different signature, it's better to use static factory as using the different method name is simpler for other people to distinguish the difference.
Unlike constructors, static factory is not required to create a new object each time they're invoked. This could improve the performance if the equivalent objects are requested often. Similar to singleton. This will also improve the performance of equals() - Don't need to override hasCode() or hash() anymore.
Static factory could return an object of any subtype of their return type. Constructor only can return the current object. This improves the flexibility of choosing the class of the returned object. 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.
public class A { private A() throws Exception { throw new Exception(); } }
public static <E extends Enum> EnumSet noneOf(Class elementType) {
Enum<?>[] universe = getUniverse(elementType);
if (universe == null)
throw new ClassCastException(elementType + " not an enum");
// Builder Pattern public class NutritionFacts {
}
// Enum singleton - the preferred approach public enum Elvis { INSTANCE; public void leaveTheBuilding() { ... } }
// Noninstantiable utility class public class UtilityClass { // Suppress default constructor for noninstantiability private UtilityClass() { throw new AssertionError(); } ... // Remainder omitted }
// Pass the dependency to the constructor to initial different field in that class - Dependency injection public class SpellChecker { private final Lexicon dictionary; public SpellChecker(Lexicon dictionary) { this.dictionary = Objects.requireNonNull(dictionary); } public boolean isValid(String word) { ... } public List suggestions(String typo) { ... }
}
// The performance can be greatly improved because every time this method is called, // a new Pattern is created and the regex is being compiled. It takes a lot of time. static boolean isRomanNumeral(String s) { return s.matches("^(?=.)M*(C[MD]|D?C{0,3})"
// The improvement version here, there will be only one Pattern which can be reused many times // It also improves the readability of this class private static final Pattern ROMAN = Pattern.compile( "^(?=.)M*(C[MD]|D?C{0,3})"
// Try to use primitive type instead of autoboxing. private static long sum() { Long sum = 0L; // many Long objects will be created in the loop, should use long instead for (long i = 0; i <= Integer.MAX_VALUE; i++) sum += i; return sum; }
public class Stack { private Object[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16;
}
public Object pop(){ if(size==0) throw new EmptyStackException(); Object result=elements[--size]; elements[size]=null; // Eliminate obsolete reference return resul; }
// resource needs to be closed in different finally block. It's hard to read and understand. static void copy(String src, String dst) throws IOException { InputStream in = new FileInputStream(src); try { OutputStream out = new FileOutputStream(dst); try { byte[] buf = new byte[BUFFER_SIZE]; int n; while ((n = in.read(buf)) >= 0) out.write(buf, 0, n); } finally { out.close(); } } finally { in.close(); } }
static String firstLineOfFile(String path) throws IOException { try (BufferedReader br = new BufferedReader(new FileReader(path))){ return br.readLine(); } }
static void copy(String src, String dst) throws IOException { try (InputStream in = new FileInputStream(src); OutputStream out = new FileOutputStream(dst)) { byte[] buf = new byte[BUFFER_SIZE]; int n; while ((n = in.read(buf)) >= 0) out.write(buf, 0, n); } }