Cách ghi đè phương thức bằng trong Java


108

Tôi đang cố gắng ghi đè phương thức bằng trong Java. Tôi có một lớp Peoplevề cơ bản có 2 trường dữ liệu nameage. Bây giờ tôi muốn ghi đè equalsphương thức để tôi có thể kiểm tra giữa 2 đối tượng People.

Mã của tôi như sau

public boolean equals(People other){
    boolean result;
    if((other == null) || (getClass() != other.getClass())){
        result = false;
    } // end if
    else{
        People otherPeople = (People)other;
        result = name.equals(other.name) &&  age.equals(other.age);
    } // end else

    return result;
} // end equals

Nhưng khi tôi viết age.equals(other.age)nó cho tôi lỗi vì phương thức bằng chỉ có thể so sánh Chuỗi và tuổi là Số nguyên.

Giải pháp

Tôi đã sử dụng ==toán tử như được đề xuất và vấn đề của tôi đã được giải quyết.


3
Này, thế còn this.age == other.age? :)
denis.solonenko

1
Kiểu dữ liệu cho độ tuổi là gì? int HOẶC Số nguyên? Ngoài ra, bạn đang sử dụng phiên bản JDK nào?
Manish

2
"as equals method chỉ có thể so sánh String" - Ai nói với bạn phương thức equals chỉ có thể so sánh String? Phương thức equals thuộc về lớp Object và bất kỳ lớp nào được tạo sẽ có sự thực thi bằng theo mặc định. Bạn có thể gọi bình đẳng trên bất kỳ lớp Java
Manish

Câu trả lời:


127
//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;
        }
    }

Đầu ra:

chạy:

- Subash Adhikari - VS - K sai

- Subash Adhikari - VS - StackOverflow sai

- Subash Adhikari - VS - Subash Adhikari true

- K - VS - StackOverflow sai

- K - VS - Subash Adhikari sai

- StackOverflow - VS - Subash Adhikari sai

- XÂY DỰNG THÀNH CÔNG (tổng thời gian: 0 giây)


7
những gì là hash = 53 * hashlý do tại sao bạn đang sử dụng đó?
kittu

2
Việc sử dụng getClass()sẽ gây ra vấn đề nếu lớp được phân lớp con và được so sánh với một đối tượng của siêu lớp.
Tuxdude

1
có thể bcoz 53số nguyên tố , có một cái nhìn tại câu trả lời này stackoverflow.com/a/27609/3425489 , ông nhận xét khi lựa chọn con số tronghashCode()
Shantaram Tupe

1
Câu trả lời chiến thắng cho câu hỏi này có một lời giải thích tuyệt vời về lý do tại sao bạn ghi đè hashCode () stackoverflow.com/a/27609/1992108
Pegasaurus

7
Cân nhắc sử dụng if (getClass ()! = Obj.getClass ()) ... thay vì sử dụng instanceoftoán tử hoặc isAssignableFrom. Điều này sẽ yêu cầu đối sánh loại chính xác, thay vì đối sánh loại phụ. - Yêu cầu đối xứng. Ngoài ra để so sánh Stringhoặc các loại Đối tượng khác, bạn có thể sử dụng Objects.equals(this.name,other.name).
YoYo

22

Giới thiệu một chữ ký phương thức mới thay đổi các kiểu tham số được gọi là nạp chồng :

public boolean equals(People other){

Ở đây Peoplekhác hơn Object.

Khi một chữ ký phương thức vẫn giống với chữ ký của lớp cha của nó, nó được gọi là ghi đè@Overridechú thích giúp phân biệt cả hai tại thời điểm biên dịch:

@Override
public boolean equals(Object other){

Nếu không nhìn thấy khai báo thực tế của age, rất khó để nói tại sao lỗi xuất hiện.


18

Tôi không chắc về chi tiết vì bạn chưa đăng toàn bộ mã, nhưng:

  • nhớ để ghi đè lên hashCode()cũng
  • các equalsphương pháp nên có Object, chứ không phải Peoplenhư kiểu đối số của nó. Hiện tại, bạn đang ghi đè, không ghi đè, phương thức bằng, có thể không phải là những gì bạn muốn, đặc biệt là khi bạn kiểm tra loại của nó sau này.
  • bạn có thể sử dụng instanceofđể kiểm tra xem nó có phải là một đối tượng People, ví dụ:if (!(other instanceof People)) { result = false;}
  • equalsđược sử dụng cho tất cả các đối tượng, nhưng không phải là nguyên thủy. Tôi nghĩ bạn có nghĩa là tuổi là một int(nguyên thủy), trong trường hợp đó chỉ cần sử dụng ==. Lưu ý rằng một Số nguyên (với chữ 'I' viết hoa) là một Đối tượng nên được so sánh với bằng.

Xem Những vấn đề nào cần được xem xét khi ghi đè bằng và Mã băm trong Java? để biết thêm chi tiết.


12
@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
}

12

Khoản 10: Tuân theo hợp đồng chung khi ghi đè bằng

Theo Effects Java , Ghi đè equalsphương pháp có vẻ đơn giản, nhưng có nhiều cách để làm sai và hậu quả có thể rất nghiêm trọng. Cách dễ nhất để tránh các vấn đề là không ghi đè equalsphương thức, trong trường hợp này mỗi thể hiện của lớp chỉ bằng với chính nó. Đây là điều đúng đắn cần làm nếu áp dụng bất kỳ điều kiện nào sau đây:

  • Mỗi cá thể của lớp vốn là duy nhất . Điều này đúng đối với các lớp như Thread đại diện cho các thực thể đang hoạt động hơn là các giá trị. Việc triển khai bằng được cung cấp bởi Object có chính xác hành vi phù hợp cho các lớp này.

  • Lớp học không cần cung cấp một bài kiểm tra “bình đẳng logic”. Ví dụ: java.util.regex.Pattern có thể đã ghi đè bằng để kiểm tra xem hai cá thể Mẫu có biểu thị chính xác cùng một biểu thức chính quy hay không, nhưng các nhà thiết kế không nghĩ rằng khách hàng sẽ cần hoặc muốn chức năng này. Trong những trường hợp này, việc triển khai bằng được kế thừa từ Object là lý tưởng.

  • Một lớp cha đã được ghi đè bằng bằng, và hành vi của lớp cha phù hợp với lớp này. Ví dụ: hầu hết các triển khai Set kế thừa triển khai bằng của chúng từ AbstractSet, triển khai Danh sách từ AbstractList và triển khai Bản đồ từ AbstractMap.

  • Lớp là private hoặc package-private và bạn chắc chắn rằng phương thức bằng của nó sẽ không bao giờ được gọi. Nếu bạn cực kỳ sợ rủi ro, bạn có thể ghi đè phương thức bằng để đảm bảo rằng nó không vô tình được gọi:

Các equalsphương pháp thực hiện một quan hệ tương đương. Nó có các thuộc tính sau:

  • Phản xạ: Đối với bất kỳ giá trị tham chiếu không rỗng nào x, x.equals(x)phải trả về true.

  • Đối xứng: Đối với mọi giá trị tham chiếu không rỗng xy, x.equals(y)phải trả về true nếu và chỉ khi y.equals (x) trả về true.

  • Bắc cầu: Đối với bất kỳ giá trị tham chiếu không null x, y, z, nếu x.equals(y)lợi nhuận truey.equals(z)lợi nhuận true, sau đó x.equals(z)phải trả lại true.

  • Nhất quán: Đối với mọi giá trị tham chiếu không rỗng xynhiều lệnh gọi x.equals(y)phải trả về truenhất quán hoặc trả về nhất quán false, miễn là không có thông tin nào được sử dụng trong so sánh ngang bằng được sửa đổi.

  • Đối với bất kỳ giá trị tham chiếu không rỗng nào x, x.equals(null)phải trả về false.

Đây là công thức cho một phương pháp bằng chất lượng cao:

  1. Sử dụng ==toán tử để kiểm tra xem đối số có phải là tham chiếu đến đối tượng này hay không. Nếu vậy, trả về true. Đây chỉ là một tối ưu hóa hiệu suất nhưng là một trong những việc đáng làm nếu việc so sánh có thể tốn kém.

  2. Sử dụng instanceoftoán tử để kiểm tra xem đối số có đúng kiểu không. Nếu không, trả về false. Thông thường, loại đúng là lớp mà phương thức xảy ra. Đôi khi, nó là một số giao diện được thực hiện bởi lớp này. Sử dụng giao diện nếu lớp triển khai giao diện tinh chỉnh hợp đồng bằng để cho phép so sánh giữa các lớp triển khai giao diện. Giao diện bộ sưu tập như Set, List, Map và Map.Entry có thuộc tính này.

  3. Truyền đối số đến đúng loại. Bởi vì quá trình ép kiểu này được thực hiện trước bởi một thử nghiệm instanceof, nó được đảm bảo sẽ thành công.

  4. Đối với mỗi trường “quan trọng” trong lớp, hãy kiểm tra xem trường đó của đối số có khớp với trường tương ứng của đối tượng này hay không. Nếu tất cả các thử nghiệm này thành công, trả về true; nếu không, trả về false. Nếu kiểu ở Bước 2 là giao diện, bạn phải truy cập các trường của đối số thông qua các phương thức giao diện; nếu loại là một lớp, bạn có thể truy cập trực tiếp vào các trường, tùy thuộc vào khả năng truy cập của chúng.

  5. Đối với các trường nguyên thủy có kiểu không phải là floathoặc double, hãy sử dụng ==toán tử để so sánh; đối với các trường tham chiếu đối tượng, hãy gọi equalsphương thức một cách đệ quy; đối với floatcác trường, sử dụng Float.compare(float, float)phương thức tĩnh ; và cho doublecác trường, sử dụng Double.compare(double, double). Việc điều trị đặc biệt của phao và các lĩnh vực đôi được làm cần thiết bởi sự tồn tại của Float.NaN, -0.0fvà các giá trị tăng gấp đôi tương tự; Mặc dù bạn có thể so sánh floatdoublecác trường với các phương thức tĩnh Float.equalsDouble.equals, điều này sẽ dẫn đến việc tự động đóng hộp trên mọi so sánh, điều này sẽ có hiệu suất kém. Đối với arraycác trường, hãy áp dụng các nguyên tắc này cho từng phần tử. Nếu mọi phần tử trong trường mảng đều quan trọng, hãy sử dụng một trong các Arrays.equalsphương pháp.

  6. Một số trường tham chiếu đối tượng có thể chứa hợp pháp null. Để tránh khả năng xảy ra NullPointerException, hãy kiểm tra các trường đó xem có bằng nhau hay không bằng cách sử dụng phương thức tĩnh 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
    
    }
    

1
Đừng quên đề cập rằng bạn cũng phải ghi đè hashCode(). Cũng lưu ý, rằng kể từ khi Java7 viết equals()hashCode()phương pháp đã trở thành dễ dàng hơn nhiều bằng cách sử dụng Objects.equals(), Arrays.equals()Objects.hashCode(), Arrays.hashCode().
Arnold Schrijver

3
Cân nhắc sử dụng if (getClass() != obj.getClass()) ...thay vì sử dụng toán tử instanceof. Điều này sẽ yêu cầu đối sánh loại chính xác , thay vì đối sánh loại phụ. - Yêu cầu đối xứng.
YoYo

@YoYo đúng ... sử dụng instanceof có thể làm hỏng thuộc tính đối xứng. Nếu o là một lớp con của PhoneNumber như có thể là PhoneNumberWithExtension, và nó ghi đè bằng cùng một cách bằng cách sử dụng instanceof, thì o.equals (this) sẽ không thực hiện được thử nghiệm instanceof trong khi PhoneNumber.equals sẽ vượt qua nó và trả về true (giả sử tất cả các trường PhoneNumber khác bằng nhau).
ldkronos

5

Vì tôi đoán agelà loại 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

Điều này sẽ dẫn đến một NullPointerExceptionif namenull.
orien

@orien Không phải là một vấn đề lớn, có thể là trong hợp đồng namekhông bao giờ được chỉ định một nullgiá trị ...
fortran

@fortran Vì vậy ... có lẽ nó không phải là một vấn đề lớn;)
orien

5

Khi so sánh các đối tượng trong Java, bạn kiểm tra ngữ nghĩa , so sánh kiểu và trạng thái xác định của các đối tượng với:

  • chính nó (cùng một trường hợp)
  • chính nó (sao chép hoặc bản sao tái tạo)
  • các đối tượng khác thuộc các loại khác nhau
  • các đối tượng khác cùng loại
  • null

Quy tắc:

  • Đối xứng :a.equals(b) == b.equals(a)
  • equals()luôn mang lại truehoặc false, nhưng không bao giờ là một NullpointerException, ClassCastExceptionhoặc bất kỳ thứ gì khác có thể ném

So sánh:

  • Kiểm tra kiểu : cả hai trường hợp cần phải cùng kiểu, nghĩa là bạn phải so sánh các lớp thực tế để có sự bình đẳng. Điều này thường không được triển khai chính xác, khi các nhà phát triển sử dụng instanceofđể so sánh kiểu (chỉ hoạt động miễn là không có lớp con và vi phạm quy tắc đối xứng khi A extends B -> a instanceof b != b instanceof a).
  • Kiểm tra ngữ nghĩa của trạng thái xác định : Đảm bảo bạn hiểu trạng thái nào mà các cá thể được xác định. Người đó có thể được xác định bằng số an sinh xã hội của họ, nhưng không phải bằng màu tóc (có thể nhuộm), tên (có thể thay đổi) hoặc tuổi (thay đổi liên tục). Chỉ với các đối tượng giá trị, bạn mới nên so sánh trạng thái đầy đủ (tất cả các trường không phải là trường tạm thời), nếu không chỉ kiểm tra những gì xác định cá thể.

Đối với Personlớp học của bạn :

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);
}

Loại tiện ích chung, có thể tái sử dụng:

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)));
    }
}

Đối với Personlớp của bạn , sử dụng lớp tiện ích này:

public boolean equals(Object obj) {
    return Equals.as(this, obj, t -> t.name, t -> t.age);
}

1

nếu age là int, bạn nên sử dụng == nếu nó là đối tượng Integer thì bạn có thể sử dụng equals (). Bạn cũng cần triển khai phương thức mã băm nếu bạn ghi đè bằng. Chi tiết của hợp đồng có sẵn trong javadoc của Object và cũng có thể ở các trang khác nhau trên web.


0

Đây là giải pháp mà tôi đã sử dụng gần đây:

public class Test {
    public String a;
    public long b;
    public Date c;
    public String d;
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof Test)) {
            return false;
        }
        Test testOther = (Test) obj;
        return (a != null ? a.equals(testOther.a) : testOther.a == null)
                && (b == testOther.b)
                && (c != null ? c.equals(testOther.c) : testOther.c == null)
                && (d != null ? d.equals(testOther.d) : testOther.d == null);
    }

}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.