Generic conventions are observed when you rewrite the equals method in the tive Java: Maid: Item8 _

Source: Internet
Author: User

Although the Object class is a specific class, it is mainly used for extension. All of its nonfinal methods (equals, hashCode, toString, clone, and finalize) are designed for rewriting, so these methods have explicit general conventions. Any class that overrides these methods has the responsibility to follow these general conventions; if this is not done, other classes dependent on these Conventions cannot work together. This chapter describes how to override the nonfinal method in an Object. As Item7 has escaped finalize, this chapter will not go into details. Comparable. compareTo is not an Object method, but it has similar features, so it is also discussed in this chapter. Rewriting the equals method looks simple, but many rewrite methods may cause errors and have serious consequences. If the instance of the class only has its own equals, the simplest way to avoid this problem is not to rewrite the equals method. You can do this if any of the following conditions are met. Each instance of the class is essentially unique. [Example] A Class such as Thread that represents an active entity rather than a value. The equals implementation provided by the Object class has the correct behavior for these classes. Does not care whether the class provides the "logical equality" test function. [Example] For example, java. util. Random can rewrite equals to check whether two Random instances produce the same Random number sequence, but the designer does not think the client will need such a function. In these cases, the equals implementation inherited from the Object is sufficient. The parent class has already rewritten equals, and the behavior of the parent class is also suitable for subclass. [Example] For example, most of the Set implementation classes inherit the equals method from AbstractSet. The List Implementation class inherits the equals method from AbstractList, and the Map implementation class inherits the equals method from AbstractMap. Class is private, or package-level private, and is sure that its equals method will never be called. The equals method of Arguably should be rewritten to the following form to prevent accidental calls: [java] @ Override public boolean equals (Object o) {throw new AssertionError (); // Method is never called} So when should I rewrite the Object. what about equals? When a class has the concept of "logical equality" and its parent class does not override equals to implement the expected behavior, you need to override Object. equals. This is usually the case for value classes. value classes refer to classes that represent "values", such as Integer or Date. Programmers use the equals method to determine whether the instances of value classes are logically equal, rather than determining whether they point to the same object. The method of rewriting equals not only meets the requirements of programmers, but also enables these instances to be map keys or set elements and have predictable and expected behaviors. There is a value class that does not need to override the equals method: the instance-controlled class (Item1), each of which has at most one object. [Example] The Enum type is such a value class. For these classes, equal logic is equivalent to equal Object, so the function of the Object. equals method is equivalent to the logical equals method. When you override the equals method, you must follow its general conventions. The equals method implements the equivalence relation (Reflexive): For any non-null references x, x. all equals (x) must return true. Symmetric Ric: For any non-null references x and y, x. equals (y) must be true only when y. equals (x) is true. Transitive: For any non-null references x, y, and z, if x. equals (y) is true, y. if equals (z) is true, x. equals (z) returns true. Consistent: For any non-null reference x and y, as long as the information used in the equals comparison operation is not modified, x. equals (y) always returns true, or always returns false. Non-null (Non-Nullity): For any Non-null references, x. equals (null) must return false. 1) The first requirement of Reflexive is that the object must be equal to itself. It is hard to imagine that this agreement will be inadvertently violated. If you violate this convention and add the instance to the set, the contains method of the set will tell you that the set does not contain the instance you just added. 2) Symmetric The second requirement is that the two objects must be consistent with "are they equal. Unlike the first article, it is hard to imagine that it has no intention of violating this requirement. [Example] the following class implements a case-sensitive string. The case of the string is saved in toString, but it is ignored during comparison. [Java] // Broken-violates every Ry! Public final class CaseInsensitiveString {private final String s; public CaseInsensitiveString (String s) {if (s = null) throw new NullPointerException (); this. s = s;} // Broken-violates sorry! @ 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;} The equals method has good intentions and is naive to try to interoperate with common strings. Assume there are two objects: [java] CaseInsensitiveString cis = new CaseInsensitiveString ("Polish"); String s = "polish"; cis. equals (s) returns true, but s. equals (cis) returns false, apparently violating symmetry. Suppose you put it into the set: [java] List <CaseInsensitiveString> list = new ArrayList <CaseInsensitiveString> (); list. add (cis); list. what will be returned by contains (s? Nobody knows! In Sun's current implementation, it happens to return false, but in other implementations, it may also return true or throw a runtime exception. Once you violate the equals conventions, when other objects face your objects, you cannot know what the behavior of these objects will be. [Solution] to eliminate this problem, you only need to remove the poor interoperability with strings in the equals method: -- to avoid violation of symmetry, equals () the parameter only takes into account the current class situation [java] @ Override public boolean equals (Object o) {return o instanceof CaseInsensitiveString & (CaseInsensitiveString) o ). s. equalsIgnoreCase (s);} 3) Transitive. It is also hard to imagine that the request has no intention of violating this rule. [Example] if a new value component (value component) is added to the subclass Based on the parent class, that is, adding information to the subclass will affect the equals comparison. We start with a simple and unchangeable two-dimensional integer point class. [Java] 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 = null |! (O instanceof Point) {return false;} Point p = (Point) o; return p. x = x & p. y = y ;}} then extends the class and adds the Color Concept: [java] class ColorPoint extends Point {private final color Color; public ColorPoint (int x, int y, color c) {super (x, y); this. how should I write the equals of the color = c ;}} subclass? If this parameter is not specified, it is inherited from the Point class, and the color information is ignored when compared with equals. Although this does not violate the equals conventions, it is obviously unacceptable. [Attempt 1] It is assumed that true is returned only when the equals parameter is another ColorPoint and has the same position and color information. [Java] // violation of symmetry with the parent class !! @ Override public boolean equals (Object o) {if (! (O instanceof ColorPoint) {return false;} ColorPoint p = (ColorPoint) o; return super. equals (p) & p. color = color;} Q: the problem is that the symmetry is violated! [Java] Point p = new Point (1, 2); ColorPoint cp = new ColorPoint (1, 2, Color. RED); p. equals (cp) returns true, while cp. equals (c) returns false. [Try 2] to solve this problem, you may try to use ColorPoint. when equals performs a hybrid comparison, the color information is ignored: [java] @ Override public boolean equals (Object o) {if (! (O instanceof Point) {return false;} if (! (O instanceof ColorPoint2) {// when comparing with a Point instance, Color return super is not considered. equals (o);} ColorPoint2 p = (ColorPoint2) o; return super. equals (p) & p. color. equals (color);} Q: This method does provide symmetry, but sacrifices the transmission. [Java] ColorPoint p1 = new ColorPoint (1, 2, Color. RED); Point p2 = new Point (1, 2); ColorPoint p3 = new ColorPoint (1, 2, Color. BLUE); p1.equals (p2), p2.equals (p3) both return true, while p1.equals (p3) returns false! The first two comparisons ignore the color information, while the third comparison considers the color information. [Try 3] what should I do? In fact, this is a basic problem of the moderate price relationship of OO language. You cannot add a value component when extending the instantiated class, and guarantee the equals convention. You may have heard that using getCalss in the equals method instead of instanceof can increase the value of the group price when the class can be instantiated and ensure the equals convention. For example: [java] // problem: violation of the Lee's replacement principle @ Override public boolean equals (Object o) {if (o = null | o. getClass ()! = GetClass () {// If o is its subclass, the permanent range is false return false;} Point p = (Point) o; return p. x = x & p. the effect of y = y;} Is that the comparison is performed only when two objects share the same implementation class. It looks good, but the consequences are unacceptable. Suppose we want to write a method to determine whether an integer is in the unit circle: [java] // Initialize UnitCircle to contain all Points on the unit circle private static final Set <Point> unitCircle; static {unitCircle = new HashSet <Point> (); unitCircle. add (new Point (1, 0); unitCircle. add (new Point (0, 1); unitCircle. add (new Point (-1, 0); unitCircle. add (new Point (0,-1);} public static boolean onUnitCircle (Point p) {return unitCircle. contains (P);} Now suppose you have extended the Point but do not add value components. For example, you can record the total number of instances created in the constructor: [java] public class CounterPoint extends Point {private static final AtomicInteger counter = new AtomicInteger (); public CounterPoint (int x, int y) {super (x, y); counter. incrementAndGet ();} public int numberCreated () {return counter. get () ;}} then we pass the CounterPoint instance to the onUnitCircle method. No matter what the x and y values in the CounterPoint are, only false is returned. Because the collection framework (such as HashSet) used in CounterPoint, The equals method is used to determine whether to include it. The CounterPoint and Point are never equivalent. -- Does not meet the Lee's replacement principle! However, if instanceof is used in the Point equals method, the same onUnitCircle method will work normally. [Solution 1] although there is no satisfactory way to expand a class that can be instantiated and add a value component, there is a way to circumvent it. According to Item16, "combination is better than inheritance", we can add a private Point domain in the ColorPoint instead of an extended Point. At the same time, we provide a public view method, returns the point object at the same position as the color Point: [java] // Adds a value component without violating the equals contract public class ColorPoint {private final Point point; private final Color; public ColorPoint (int x, int y, Color color) {if (color = null) throw new NullPointerException (); point = new Point (x, y); this. color = co Lor;}/*** Returns the point-view of this color point. */public Point asPoint () {return point;} @ Override public boolean equals (Object o) {if (! (O instanceof ColorPoint) return false; ColorPoint cp = (ColorPoint) o; return cp. point. equals (point) & cp. color. equals (color );}... // Remainder omitted} in the Java platform library, some classes extend the instantiation class and add value components. [Example] For example, java. SQL. Timestamp extends the java. util. Date class and adds the nanoseconds domain. The equals method of Timestamp violates symmetry. If Timestamp and Date are used in the same set or in any other way, this will cause an error. Timestamp has a disclaimer reminding programmers not to mix Date and Timestamp. Even if they are not mixed, they will not be in trouble, but no one can stop you from mixing them, and the errors caused by the results will be difficult to debug. Timestamp is a mistake and is not worth doing. [Java]/*** Note: This method is not using Ric with respect to the * <code> equals (Object) </code> method in the base class. */public boolean equals (java. lang. object ts) {if (ts instanceof Timestamp) {return this. equals (Timestamp) ts) ;}else {return false ;}note: You can add components to the subclass of an abstract class without violating the equals conventions. This is very important for the class hierarchy obtained based on Item20 (replacing the label class with the class hierarchy, Prefer class hierarchies totagged classes. [Example] For example, you may have an abstract class Shape without any value component. A subclass Circle adds a radius field and a subclass Rectangle adds the length and width fields. The above problem will not occur, because it is impossible to directly create a parent class instance. 4) Consistent the fourth requirement is that if two objects are equal, they must be equal at any time unless one (or two) object is modified. In other words, a mutable object can be equal to different objects at different times, but not an immutable object. When writing a class, consider whether it should be immutable (Item15 )? If the conclusion is that it should be immutable, ensure that the equals method meets the following requirements: equal objects are always equal, and unequal objects are always unequal. No matter whether the class is variable or not, the equals method cannot depend on unreliable resources. If you violate this restriction, it is difficult to meet the consistency requirements. [Example] java.net. the equals method of a URL depends on the comparison of the IP addresses of hosts in the URL. to translate the host name into an IP address, you need to access the network. The results with the same range may not be guaranteed over time. This will cause the equals method of the URL to violate the conventions and cause problems in practice. Unfortunately, this line cannot be changed due to compatibility requirements. Except for a few exceptions, the equals method must perform deterministic computing on objects residing in the memory. 5) the last Non-Nullity requirement means that all objects are not equal to null. Although it is hard to imagine under which circumstances o. equals (null) will return true, it is not hard to imagine that NullPointException is unexpectedly thrown. This is not allowed in general conventions. Many euqals methods use null to prevent this situation: [java] @ Override public boolean equals (Object o) {if (o = null) return false ;} this test is unnecessary. To test equality, the equals method must first forcibly convert the parameter to a suitable type before calling its accessors or domains. Before forced conversion, you need to use the instanceof method to check whether the parameter is of the appropriate type: [java] @ Override public boolean equals (Object o) {if (! (O instanceof MyType) return false; MyType mt = (MyType) o;} if there is no type check, when the equals parameter passes in an error type, equals will throw ClassCastException, violation. However, if the first operator of instanceof is null, false is returned. That is to say, if null is input, the type check will return false, and no separate null check is required. To sum up, there are the following tips for implementing a high-quality equals method: 1) Use the = Operator to determine whether a parameter is a reference to the object. If yes, true is returned. This is just a kind of performance optimization. It is worth doing so when the comparative operation overhead is large. 2) use the instanceof operator to determine whether the parameter is of the correct type. If not, false is returned. Generally, the correct type is the class of the equals method. Occasionally, it is an interface implemented by this class. If the equals convention is optimized for the interface used by the class, you can compare different classes that implement the interface, then use the interface. Set, List, Map, and Map. Entry interfaces all have this feature. [Java] public interface Set <E> {/*** Compares the specified object with this set for equality. returns * <tt> true </tt> if the specified object is also a set, the two sets * have the same size, and every member of the specified set is * contained in this set (or equivalently, every member of this set is * contained in the specified set). >>>>>>>> this definition ensures that the * equals method w Orks properly implements SS different implementations of the * set interface. ** @ param o object to be compared for equality with this set * @ return <tt> true </tt> if the specified object is equal to this set */boolean equals (Object o ); public abstract class implements actset <E> implements Set <E> {public boolean equals (Object o) {if (o = this) return true; if (! (O instanceof Set) return false; Collection c = (Collection) o; if (c. size ()! = Size () return false; try {return containsAll (c);} catch (ClassCastException unused) {return false;} catch (NullPointerException unused) {return false ;}}} 3) Forcibly convert the parameter to the correct type. Because the previous instanceof test was conducted, the forced conversion here can ensure the success. 4) traverse each key field in the class and check whether the field in the parameter matches the domain of the object. If the test is successful, true is returned. Otherwise, false is returned. If the type in step 2 is an interface, you need to use the interface method to access the domain in the parameter; if it is a class, you may be able to directly access the parameter domain, depending on the visibility of the domain. For basic types that are not float and double, use the = Operator for comparison; for object reference, call equals recursively; for float fields, use Float. the compare method. For the double field, Double. compare method. Special processing of float and double fields is necessary because Float. NaN,-0.0f, and similar double constants exist. For details, refer to the Float. equals method. [Java] // Float. equals/* Note that in most cases, for the two instances f1 and f2 of the Float class, the condition that the value of f1.equals (f2) is true is when and only when f1.floatValue () = the value of f2.floatValue () is also true. But there are two exceptions: 1) if both f1 and f2 indicate Float. naN, so even if Float. naN = Float. the value of NaN is false, and The equals method returns true. 2) If f1 indicates + 0.0f, f2 indicates-0.0f, or vice versa, the value returned by the equal test is false, even if the value of 0.0f =-0.0f is true. */Public boolean equals (Object obj) {return (obj instanceof Float) & (floatToIntBits (Float) obj ). value) = floatToIntBits (value);} For array fields, compare each element. If each element in the array is important, you can use the Arrays. equals method of JDK1.5. [Java] // Arrays. equals public static boolean equals (Object [] a, Object [] a2) {if (a = a2) return true; if (a = null | a2 = null) return false; int length =. length; if (a2.length! = Length) return false; for (int I = 0; I <length; I ++) {Object o1 = a [I]; Object o2 = a2 [I]; if (! (O1 = null? O2 = null: o1.equals (o2) return false;} return true;} an object reference may contain null. To avoid possible NullPointException, you can use the following regular usage for comparison: [java] (field = null? O. field = null: field. equals (o. field) If field and o. if the field is always the same reference, the following method will be faster: [java] (field = o. field | (field! = Null & field. equals (o. field) for some classes, such as the CaseInsensitiveString above, the comparison of fields is much more complicated than simple equality tests. In this case, you may want to save a standard form (canonical form) for the domain. The equals method performs precise comparison with low overhead in these standard forms, this is not an inaccurate comparison of high-availability products. This method is most suitable for immutable classes (Item15). If the object changes, it must be updated in the standard format. The sequence of domain comparison may affect the performance of the equals method. To achieve the best performance, we should first compare the most likely inconsistent domains with the least overhead. It is not necessary to compare the fields that are not in the logical state of the object, such as the Lock domain of the synchronization operation. Redundant domains should not be compared. These domains can be computed through key domains, but this may improve the performance of the equals method. If redundant fields represent a Summary of the entire object, comparing these fields can save a lot of cost. [Example] assume that there is a Polygon class and the area field is cached. If two Polygon classes have different areas, you do not need to compare their edges and vertices. 5) after completing the equals method, you can ask yourself three questions: Is it symmetric, transmitted, and consistent? In addition, we should not only ask ourselves, but also write unit tests to test these features. If it fails, locate the cause and modify the equals method accordingly. Of course, equals also needs to be full of other two features (self-inverse and non-null), but these two features are generally automatically satisfied. In Item9, PhoneNumber. equals is compiled based on the preceding tips. The following are some final notes: hashCode (Item9) must be rewritten when equals is rewritten, so that equals cannot be overly intelligent. If you simply test whether the domains are equal, it is not difficult to meet the equals conventions. When you excessively seek the equivalence relationship, it is easy to get into trouble. For example, the File class should not treat the symbolic links pointing to the same File as equal objects. Fortunately, the File class does not. Do not replace objects with other types in the equals method declaration. To prevent this, @ Override should be used every time equals is rewritten.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.