Valid Java: common methods for all objects

Source: Internet
Author: User

Valid Java: common methods for all objects
Preface:

When I read the 1st rules of this book, I felt that this is a good book. I still recommend that we upgrade our Java skills to a higher level. Here, I will take a note summary about covering the equals, hashCode, and toString methods, hoping to share with you.

 

Overview:

This chapter mainly describes some methods that are common to all objects. We know that Java polymorphism is one of its characteristics, and the embodiment of polymorphism is called "Rewriting ". These conceptual things I want to learn at the beginning of Java in college, the teacher will instill them into us like a few treasures. However, at that time, how many people really knew what is heavy load, what is rewriting and what is polymorphism?

For some developers, it is common to understand and use them. But are you sure you know enough?

 

Related content:

 

1. comply with general conventions when overwriting equals

We know that equals is used to compare two objects in Java if they are equal. For beginners, there may be more differences between equals and =. At the beginning, everyone may be confused, this may be because you do not have the concept of address and value. For the difference between equals and =, you can refer to the difference between equals and = in Java in this blog.

If you are not quite clear about the differences between equals and =, you can take a few minutes to read the above blog so that you can understand why we need to override the equals method. If you fully understand it, there is nothing to stop you from looking at it.

 

We know that equals is implemented logically. From the mathematical point of view, there are several equal conditions for two things:

1. Self-inverse: true must be returned for any non-null reference values x, x. equals (x.

2. Symmetry: for non-null reference values x and y, if and only if x. equals (y) returns true, y. equals (x) must return true.

3. passed: For any non-null reference values x, y, z, if x. equals (y) = true, y. equals (z) = true, then x. equals (z) must also return true.

4. consistency: For any non-null reference values x and y, as long as the information used in the comparison operation of equals in the object is not modified, x is called multiple times. equals (y) returns true consistently or false consistently.

5. For non-null reference values x, x. equals (null) must return false.

After reading the above mathematical rules, do you have the feeling of being so troublesome? Intuitively, these rules are indeed troublesome, but you cannot ignore them.

 

Below I will explain these rules through some instance learning.

1. Self-defense:

 

public static void equalsOppositeSelf() {        String s = ABC;        Object o = new Object();                System.out.println(s.equals(s));       
Result:
truetrue

 

2. Symmetry:

You may take symmetry for granted. This is because in your opinion, the two we want to compare must be of the same type. This must be too idealistic. What if the two objects we compare are not of the same type? Let's take a look at this example.

public final class CaseInsensitiveString {    private final String s;        public CaseInsensitiveString(String s) {        if (s == null) {            throw new NullPointerException();        }                this.s = s;    }        public boolean equals(Object o) {        if (o instanceof CaseInsensitiveString) {            return s.equalsIgnoreCase(((CaseInsensitiveString)o).s);        }                if (o instanceof String) {            return s.equalsIgnoreCase((String)o);        }                return false;    }}
The code of the equals method above allows you to compare strings by case-insensitive comparison. We do not consider comparing two objects of the same type. For two objects of different types, we can see from the code above that if the object to be compared is of the String type, we can ignore the case sensitivity for comparison, and the answer is reasonable. Let's look at the example below:

Comparison Method:

public static void equalsSymmetric() {        CaseInsensitiveString s1 = new CaseInsensitiveString(abc);        String s2 = abc;                System.out.println(s1 == s2 ?  + s1.equals(s2));        System.out.println(s2 == s1 ?  + s2.equals(s1));    }

Comparison result:

s1 == s2 ? trues2 == s1 ? false

Why? Isn't equals satisfying symmetry? Why doesn't it work again?

After careful consideration, we can find that when we perform s1.equals (s2), it is because s1 is of the CaseInsensitiveString type and it will execute the above Code, while s2 is of the String type, the comparison of s2.equals (s1) is naturally the equals method in String.

Then you will ask, in this case, we cannot modify the code in the String class. If you think so, I will be speechless. One thing we know is that two objects of different types are different. That is to say, we need to have a judgment program. If the compared object is not of the CaseInsensitiveString type, then we can simply return false. The modified code is as follows:

    public boolean equals(Object o) {        return o instanceof CaseInsensitiveString && ((CaseInsensitiveString) o).s.equalsIgnoreCase(s);    }

3. Transmission

If the value of x = y and y = z is passed, the value of x = z can be determined.

Now let's assume that we have a class Point and a subclass ColorPoint of a Point are as follows:

Point

public class Point {    private final int x;        private final int y;        public Point(int x, int y) {        this.x = x;        this.y = y;    }        public boolean equals(Object o) {        if (!(o instanceof Point)) {            return false;        }                Point p = (Point) o;                return p.x == x && p.y == y;    }}

 

ColorPoint

public class ColorPoint extends Point {    private final Color color;        public ColorPoint(int x, int y, Color color) {        super(x, y);        this.color = color;    }    }

We can see that ColorPoint inherits from Point, but it has one more color attribute than the Point class. When we compare ColorPoint with Point and Point with ColorPoint, as follows:
public static void equalsTransitivity() {        Point p1 = new Point(1, 2);        ColorPoint cp1 = new ColorPoint(1, 2, Color.BLACK);                System.out.println(p1 == cp1 ?  + p1.equals(cp1));        System.out.println(cp1 == p1 ?  + cp1.equals(p1));    }

The following result is displayed:

 

p1 == cp1 ? truecp1 == p1 ? true

Why are both true? It is clear that there are two different types. If you really want to consider the relationship between the parent class and the subclass, it should also be true or false. Here, the ColorPoint itself does not overwrite the Point equals. It uses the Point equals. At this time, no matter which comparison, x and y are compared, it is irrelevant to color.

This will cause a problem. What if both of my comparison objects are ColorPoint? In this way, if the x and y values of two colorpoints are the same, but the color is different, the result value will be true no matter how we compare them. color will not be checked here. Then you may say, We will rewrite the ColorPoint equals.

 

Here we use a suggestion: compound is better than inheritance (this is also reflected in the design model ).

Example:

public static void equalsTransitivity() {        Point p1 = new Point(1, 2);        ColorPoint cp1 = new ColorPoint(1, 2, Color.BLACK);        ColorPoint cp2 = new ColorPoint(1, 2, Color.BLUE);                ColorPointNew cpn1 = new ColorPointNew(1, 2, Color.BLACK);        ColorPointNew cpn2 = new ColorPointNew(1, 2, Color.BLUE);                System.out.println(p1 == cp1 ?  + p1.equals(cp1));        System.out.println(cp1 == p1 ?  + cp1.equals(p1));        System.out.println(cp1 == cp2 ?  + cp1.equals(cp2));                System.out.println(cpn1 == cpn2 ?  + cpn1.equals(cpn2));        System.out.println(cpn1 == cp1 ?  + cpn1.equals(cp1));        System.out.println(cp1 == cpn1 ?  + cp1.equals(cpn1));    }

 

Result:

p1 == cp1 ? truecp1 == p1 ? truecp1 == cp2 ? truecpn1 == cpn2 ? falsecpn1 == cp1 ? falsecp1 == cpn1 ? false
The above code looks very concise.

 

4. Consistency

Consistency requires that if two objects are equal, they must always be equal unless one of them is modified.

 

2. overwrite the hashCode when overwriting equals.

Why overwrite the hashCode when overwriting equals? Aren't all the things we mentioned above good? Aren't all necessary mathematical rules of equals satisfied? Have we done almost the same thing? Yes, it is indeed similar, but we still need to overwrite the hashCode method. This is because if we associate our objects with Hash values such as HashMap, we may be confused or even disappointed. Next, let's look at an example. It is better to explain it based on the example.

We have such a PhoneNumber class:

 

package com.java.effective.samples;public final class PhoneNumber {    private final short areaCode;        private final short prefix;        private final short lineNumber;        public PhoneNumber(int areaCode, int prefix, int lineNumber) {        rangeCheck(areaCode, 999,  area code);        rangeCheck(prefix, 999,  prefix);        rangeCheck(lineNumber, 9999,  line number);                this.areaCode = (short)areaCode;        this.prefix = (short)prefix;        this.lineNumber = (short)lineNumber;    }        private static void rangeCheck(int arg, int max, String name) {        if (arg < 0 || arg > max) {            throw new IllegalArgumentException(name + :  + arg);        }    }        @Override    public boolean equals(Object o) {        if (o == this) {            return true;        }                if (!(o instanceof PhoneNumber)) {            return false;        }                PhoneNumber pNumber = (PhoneNumber)o;                return (pNumber.lineNumber == lineNumber) && (pNumber.prefix == prefix) && (pNumber.areaCode == areaCode);    }}
The above code processes equals completely OK. But what if we put PhoneNumber and HashMap together? Below are our test cases:

 

 

public static void hashCodePhoneNumber() {        Map
 
   map = new HashMap
  
   ();        PhoneNumber phoneNumber = new PhoneNumber(707, 867, 9876);        map.put(phoneNumber, Jenny);                System.out.println(map.get(new PhoneNumber(707, 867, 9876)));        System.out.println(map.get(phoneNumber));    }
  
 

 

 

Result:

nullJenny

 

 

We can understand the above map. put (). If we do not overwrite the hashCode, when we use map. put, we put these PhoneNumber objects in different boxes, and we go to map. in get (), you just need to find it in a certain box (of course, if map. get () and map. if the object in put () is the same, you can find it ).

If we overwrite the hashCode method, if the value calculated by hashCode is equal, it will be placed in the same box. In this way, as long as the values saved in our object are completely consistent, the value corresponding to this key will be found. I don't know if you find it. This hashCode is a bit similar to classification, which greatly improves the efficiency when the data volume is large.

We can override the hashCode method using the following two methods:

Method 1:

 

@Override    public int hashCode() {        return 42;    }

Method 2:

 

 

@Override    public int hashCode() {        int result = 17;        result = 31 * result + areaCode;        result = 31 * result + prefix;        result = 31 * result + lineNumber;        return result;    }

 

First, both methods are supported. Through the above analysis, from the perspective of efficiency, of course, the second method is more appropriate.

So while overwriting equlas, do not forget to overwrite hashCode.

 

3. always overwrite toString

Take the PhoneNumber class for example. If we do not overwrite the toString () method of the class, the consequence is that when we need to print the object of this class, there will be something we don't want. Similar to this: com. java. Valid tive. samples. PhoneNumber @ 12a7e3

Sometimes we don't want to print such objects, so we have to overwrite their toString method. In this method, we can add the toString Method to the class as needed. For PhoneNumber, we can write as follows:

 

@Override    public String toString() {        String result = ;        result += (areaCode + -);        result += (prefix + -);        result += (lineNumber);                return result;    }

 

 

Print result:

 

707-867-9876

 

Summary:

When optimizing the code, consider overwriting these methods reasonably to make our code more robust.

 

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.