How to override equals method in Java

后端 未结 9 1574
自闭症患者
自闭症患者 2020-11-22 01:41

I am trying to override equals method in Java. I have a class People which basically has 2 data fields name and age. Now I want to ove

相关标签:
9条回答
  • 2020-11-22 01:59

    if age is int you should use == if it is Integer object then you can use equals(). You also need to implement hashcode method if you override equals. Details of the contract is available in the javadoc of Object and also at various pages in web.

    0 讨论(0)
  • 2020-11-22 02:01
    //Written by K@stackoverflow
    public class Main {
    
        /**
         * @param args the command line arguments
         */
        public static void main(String[] args) {
            // TODO code application logic here
            ArrayList<Person> people = new ArrayList<Person>();
            people.add(new Person("Subash Adhikari", 28));
            people.add(new Person("K", 28));
            people.add(new Person("StackOverflow", 4));
            people.add(new Person("Subash Adhikari", 28));
    
            for (int i = 0; i < people.size() - 1; i++) {
                for (int y = i + 1; y <= people.size() - 1; y++) {
                    boolean check = people.get(i).equals(people.get(y));
    
                    System.out.println("-- " + people.get(i).getName() + " - VS - " + people.get(y).getName());
                    System.out.println(check);
                }
            }
        }
    }
    
    //written by K@stackoverflow
    public class Person {
        private String name;
        private int age;
    
        public Person(String name, int age){
            this.name = name;
            this.age = age;
        }
    
        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
    
            if (obj.getClass() != this.getClass()) {
                return false;
            }
    
            final Person other = (Person) obj;
            if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
                return false;
            }
    
            if (this.age != other.age) {
                return false;
            }
    
            return true;
        }
    
        @Override
        public int hashCode() {
            int hash = 3;
            hash = 53 * hash + (this.name != null ? this.name.hashCode() : 0);
            hash = 53 * hash + this.age;
            return hash;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    

    Output:

    run:

    -- Subash Adhikari - VS - K false

    -- Subash Adhikari - VS - StackOverflow false

    -- Subash Adhikari - VS - Subash Adhikari true

    -- K - VS - StackOverflow false

    -- K - VS - Subash Adhikari false

    -- StackOverflow - VS - Subash Adhikari false

    -- BUILD SUCCESSFUL (total time: 0 seconds)

    0 讨论(0)
  • 2020-11-22 02:06

    When comparing objects in Java, you make a semantic check, comparing the type and identifying state of the objects to:

    • itself (same instance)
    • itself (clone, or reconstructed copy)
    • other objects of different types
    • other objects of the same type
    • null

    Rules:

    • Symmetry: a.equals(b) == b.equals(a)
    • equals() always yields true or false, but never a NullpointerException, ClassCastException or any other throwable

    Comparison:

    • Type check: both instances need to be of the same type, meaning you have to compare the actual classes for equality. This is often not correctly implemented, when developers use instanceof for type comparison (which only works as long as there are no subclasses, and violates the symmetry rule when A extends B -> a instanceof b != b instanceof a).
    • Semantic check of identifying state: Make sure you understand by which state the instances are identified. Persons may be identified by their social security number, but not by hair color (can be dyed), name (can be changed) or age (changes all the time). Only with value objects should you compare the full state (all non-transient fields), otherwise check only what identifies the instance.

    For your Person class:

    public boolean equals(Object obj) {
    
        // same instance
        if (obj == this) {
            return true;
        }
        // null
        if (obj == null) {
            return false;
        }
        // type
        if (!getClass().equals(obj.getClass())) {
            return false;
        }
        // cast and compare state
        Person other = (Person) obj;
        return Objects.equals(name, other.name) && Objects.equals(age, other.age);
    }
    

    Reusable, generic utility class:

    public final class Equals {
    
        private Equals() {
            // private constructor, no instances allowed
        }
    
        /**
         * Convenience equals implementation, does the object equality, null and type checking, and comparison of the identifying state
         *
         * @param instance       object instance (where the equals() is implemented)
         * @param other          other instance to compare to
         * @param stateAccessors stateAccessors for state to compare, optional
         * @param <T>            instance type
         * @return true when equals, false otherwise
         */
        public static <T> boolean as(T instance, Object other, Function<? super T, Object>... stateAccessors) {
            if (instance == null) {
                return other == null;
            }
            if (instance == other) {
                return true;
            }
            if (other == null) {
                return false;
            }
            if (!instance.getClass().equals(other.getClass())) {
                return false;
            }
            if (stateAccessors == null) {
                return true;
            }
            return Stream.of(stateAccessors).allMatch(s -> Objects.equals(s.apply(instance), s.apply((T) other)));
        }
    }
    

    For your Person class, using this utility class:

    public boolean equals(Object obj) {
        return Equals.as(this, obj, t -> t.name, t -> t.age);
    }
    
    0 讨论(0)
  • 2020-11-22 02:08
    @Override
    public boolean equals(Object that){
      if(this == that) return true;//if both of them points the same address in memory
    
      if(!(that instanceof People)) return false; // if "that" is not a People or a childclass
    
      People thatPeople = (People)that; // than we can cast it to People safely
    
      return this.name.equals(thatPeople.name) && this.age == thatPeople.age;// if they have the same name and same age, then the 2 objects are equal unless they're pointing to different memory adresses
    }
    
    0 讨论(0)
  • 2020-11-22 02:11

    Since I'm guessing age is of type int:

    public boolean equals(Object other){
        boolean result;
        if((other == null) || (getClass() != other.getClass())){
            result = false;
        } // end if
        else{
            People otherPeople = (People)other;
            result = name.equals(otherPeople.name) &&  age == otherPeople.age;
        } // end else
    
        return result;
    } // end equals
    
    0 讨论(0)
  • 2020-11-22 02:13

    Item 10: Obey the general contract when overriding equals

    According to Effective Java, 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:

    • 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.

    • 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.

    • 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.

    • 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:

    The equals method implements an equivalence relation. It has these properties:

    • Reflexive: For any non-null reference value x, x.equals(x) must return 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.

    • 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.

    • 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.

    • For any non-null reference value x, x.equals(null) must return false.

    Here’s a recipe for a high-quality equals method:

    1. Use the == operator to check if the argument is a reference to this object. If so, return true. This is just a performance optimization but one that is worth doing if the comparison is potentially expensive.

    2. Use the instanceof operator to check if the argument has the correct type. If not, return false. Typically, the correct type is the class in which the method occurs. Occasionally, it is some interface implemented by this class. Use an interface if the class implements an interface that refines the equals contract to permit comparisons across classes that implement the interface. Collection interfaces such as Set, List, Map, and Map.Entry have this property.

    3. Cast the argument to the correct type. Because this cast was preceded by an instanceof test, it is guaranteed to succeed.

    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.

    5. 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; 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.

    6. 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).

      // Class with a typical equals method
      
      public final class PhoneNumber {
      
          private final short areaCode, prefix, lineNum;
      
          public PhoneNumber(int areaCode, int prefix, int lineNum) {
      
              this.areaCode = rangeCheck(areaCode,  999, "area code");
      
              this.prefix   = rangeCheck(prefix,    999, "prefix");
      
              this.lineNum  = rangeCheck(lineNum,  9999, "line num");
      
          }
      
          private static short rangeCheck(int val, int max, String arg) {
      
              if (val < 0 || val > max)
      
                 throw new IllegalArgumentException(arg + ": " + val);
      
              return (short) val;
      
          }
      
          @Override public boolean equals(Object o) {
              if (o == this)
                  return true;
              if (!(o instanceof PhoneNumber))
                  return false;
              PhoneNumber pn = (PhoneNumber)o;
              return pn.lineNum == lineNum && pn.prefix == prefix
                      && pn.areaCode == areaCode;
          }
          ... // Remainder omitted
      
      }
      
    0 讨论(0)
提交回复
热议问题