Có một tiện ích phản chiếu Java để thực hiện so sánh sâu giữa hai đối tượng không?


99

Tôi đang cố gắng viết các bài kiểm tra đơn vị cho nhiều clone()hoạt động khác nhau trong một dự án lớn và tôi đang tự hỏi liệu có một lớp hiện có ở đâu đó có khả năng lấy hai đối tượng cùng loại, thực hiện một so sánh sâu và cho biết liệu chúng có giống nhau hay không?


1
Làm thế nào để lớp này biết được liệu tại một điểm nhất định của biểu đồ đối tượng, nó có thể chấp nhận các đối tượng giống hệt nhau hay chỉ các tham chiếu giống nhau?
Zed 19/09/09

Lý tưởng nhất là nó sẽ đủ cấu hình :) Tôi đang tìm kiếm thứ gì đó tự động để nếu các trường mới được thêm vào (và không được sao chép) thì thử nghiệm có thể xác định chúng.
Uri 19/09/09

3
Những gì tôi đang cố gắng nói là bạn sẽ cần phải cấu hình (tức là thực hiện) các so sánh. Vậy tại sao không ghi đè phương thức bằng trong các lớp của bạn và sử dụng phương thức đó?
Zed 19/09/09

3
Nếu bằng trả về false cho một đối tượng phức tạp lớn, bạn phải bắt đầu từ đâu? Tốt hơn hết là bạn nên biến đối tượng thành một Chuỗi nhiều dòng và thực hiện so sánh Chuỗi. Sau đó, bạn có thể thấy chính xác nơi hai đối tượng khác nhau. IntelliJ bật lên một cửa sổ so sánh "thay đổi" giúp tìm nhiều thay đổi giữa hai kết quả, tức là nó hiểu đầu ra của khẳng định (string1, string2) và cung cấp cho bạn một cửa sổ so sánh.
Peter Lawrey

Có một số câu trả lời thực sự tốt ở đây, bên cạnh một trong những được chấp nhận, mà dường như đã gạt chôn
user1445967

Câu trả lời:


62

Unitils có chức năng này:

Khẳng định bình đẳng thông qua phản chiếu, với các tùy chọn khác nhau như bỏ qua các giá trị mặc định / null của Java và bỏ qua thứ tự của các tập hợp


9
Tôi đã thực hiện một số thử nghiệm trên chức năng này và nó dường như thực hiện một phép so sánh sâu trong khi EqualsBuilder thì không.
Howard, ngày

Có cách nào để điều này không bỏ qua các trường tạm thời không?
Pinch

@Pinch Tôi nghe thấy bạn. Tôi có thể nói rằng công cụ so sánh sâu unitilschính xác là thiếu sót vì nó so sánh các biến ngay cả khi chúng có thể không có tác động quan sát được . Một hệ quả khác (không mong muốn) của việc so sánh các biến là các đóng đơn thuần (không có trạng thái của riêng chúng) không được hỗ trợ. Thêm vào đó, nó yêu cầu các đối tượng được so sánh phải có cùng kiểu thời gian chạy. Tôi xắn tay áo và tạo phiên bản công cụ so sánh sâu của riêng mình để giải quyết những mối lo ngại này.
beluchin

@Wolfgang có mã mẫu nào để hướng dẫn chúng tôi không? Bạn lấy câu trích dẫn đó từ đâu?
anon58192932

30

Tôi thích câu hỏi này! Chủ yếu là vì nó hiếm khi được trả lời hoặc trả lời không tốt. Nó giống như chưa ai tìm ra nó. Lãnh thổ trinh nữ :)

Trước hết, thậm chí không nghĩ về việc sử dụng equals. Hợp đồng equals, như được định nghĩa trong javadoc, là một quan hệ tương đương (phản xạ, đối xứng và bắc cầu), không phải là một quan hệ bình đẳng. Vì vậy, nó cũng sẽ phải là đối xứng. Việc triển khai duy nhất của equalsđiều đó là (hoặc từng có thể là) một quan hệ bình đẳng thực sự là quan hệ trong java.lang.Object. Ngay cả khi bạn đã sử dụng equalsđể so sánh mọi thứ trong biểu đồ, nguy cơ phá vỡ hợp đồng là khá cao. Như Josh Bloch đã chỉ ra trong Java hiệu quả , hợp đồng bằng rất dễ bị phá vỡ:

"Đơn giản là không có cách nào để mở rộng một lớp có thể khởi tạo và thêm một khía cạnh trong khi vẫn bảo toàn hợp đồng bằng"

Ngoài ra, phương pháp boolean có thực sự tốt không? Thật tuyệt khi thực sự gói gọn tất cả những điểm khác biệt giữa bản gốc và bản sao, bạn có nghĩ vậy không? Ngoài ra, ở đây tôi sẽ giả định rằng bạn không muốn phải bận tâm với việc viết / duy trì mã so sánh cho từng đối tượng trong biểu đồ, mà là bạn đang tìm kiếm thứ gì đó sẽ thay đổi tỷ lệ với nguồn khi nó thay đổi theo thời gian.

Soooo, những gì bạn thực sự muốn là một số loại công cụ so sánh trạng thái. Cách triển khai công cụ đó thực sự phụ thuộc vào bản chất của mô hình miền và các hạn chế về hiệu suất của bạn. Theo kinh nghiệm của tôi, không có viên đạn ma thuật chung chung. Và nó sẽ chậm qua một số lượng lớn các lần lặp lại. Nhưng để kiểm tra tính hoàn chỉnh của hoạt động sao chép, nó sẽ thực hiện công việc khá tốt. Hai lựa chọn tốt nhất của bạn là tuần tự hóa và phản chiếu.

Một số vấn đề bạn sẽ gặp phải:

  • Thứ tự bộ sưu tập: Hai bộ sưu tập có nên được coi là giống nhau nếu chúng chứa các đối tượng giống nhau, nhưng theo thứ tự khác nhau không?
  • Những trường nào cần bỏ qua: Tạm thời? Tĩnh không?
  • Kiểu tương đương: Giá trị trường có nên cùng kiểu không? Hoặc là nó ok cho một trong những mở rộng khác?
  • Còn nữa, nhưng tôi quên ...

XStream khá nhanh và kết hợp với XMLUnit sẽ thực hiện công việc chỉ trong vài dòng mã. XMLUnit rất hay vì nó có thể báo cáo tất cả sự khác biệt hoặc chỉ dừng lại ở điểm đầu tiên mà nó tìm thấy. Và đầu ra của nó bao gồm xpath tới các nút khác nhau, điều này thật tuyệt. Theo mặc định, nó không cho phép các bộ sưu tập không có thứ tự, nhưng nó có thể được cấu hình để làm như vậy. Việc tiêm một trình xử lý sự khác biệt đặc biệt (Được gọi là a DifferenceListener) cho phép bạn chỉ định cách bạn muốn xử lý sự khác biệt, bao gồm cả việc bỏ qua thứ tự. Tuy nhiên, ngay khi bạn muốn làm bất cứ điều gì ngoài tùy chỉnh đơn giản nhất, thì việc viết sẽ trở nên khó khăn và các chi tiết có xu hướng bị ràng buộc vào một đối tượng miền cụ thể.

Sở thích cá nhân của tôi là sử dụng phản xạ để duyệt qua tất cả các trường đã khai báo và đi sâu vào từng trường, theo dõi sự khác biệt khi tôi tiếp tục. Lời cảnh báo: Không sử dụng đệ quy trừ khi bạn thích ngoại lệ tràn ngăn xếp. Giữ mọi thứ trong phạm vi với một ngăn xếp (sử dụngLinkedListhoặc cái gì đó). Tôi thường bỏ qua các trường tạm thời và trường tĩnh, và tôi bỏ qua các cặp đối tượng mà tôi đã so sánh, vì vậy tôi không kết thúc trong các vòng lặp vô hạn nếu ai đó quyết định viết mã tự tham chiếu (Tuy nhiên, tôi luôn so sánh các trình bao bọc nguyên thủy bất kể điều gì , vì các ref của cùng một đối tượng thường được sử dụng lại). Bạn có thể định cấu hình mọi thứ từ trước để bỏ qua thứ tự bộ sưu tập và bỏ qua các loại hoặc trường đặc biệt, nhưng tôi muốn xác định chính sách so sánh trạng thái của mình trên chính các trường thông qua chú thích. Đây, IMHO, chính xác là ý nghĩa của các chú thích, để làm cho dữ liệu meta về lớp có sẵn trong thời gian chạy. Cái gì đó như:


@StatePolicy(unordered=true, ignore=false, exactTypesOnly=true)
private List<StringyThing> _mylist;

Tôi nghĩ rằng đây thực sự là một vấn đề thực sự khó, nhưng hoàn toàn có thể giải quyết được! Và một khi bạn có thứ gì đó phù hợp với mình, nó thực sự, thực sự, rất tiện dụng :)

Vậy thì chúc may mắn. Và nếu bạn nghĩ ra điều gì đó chỉ là thiên tài thuần túy, đừng quên chia sẻ!


15

Xem DeepEquals và DeepHashCode () trong java-use: https://github.com/jdereg/java-util

Lớp này thực hiện đúng những gì tác giả gốc yêu cầu.


4
Cảnh báo: DeepEquals sử dụng phương thức .equals () của một đối tượng nếu một đối tượng tồn tại. Đây có thể không phải là điều bạn muốn.
Adam

4
Nó chỉ sử dụng .equals () trên một lớp nếu một phương thức equals () được thêm vào một cách rõ ràng, nếu không nó sẽ thực hiện so sánh từng thành viên. Logic ở đây là nếu ai đó đã cố gắng viết một phương thức equals () tùy chỉnh thì nó nên được sử dụng. Nâng cao trong tương lai: cho phép cờ để nó bỏ qua các phương thức equals () ngay cả khi chúng tồn tại. Có những tiện ích hữu ích trong java-use, như CaseInsensitiveMap / Set.
John DeRegnaucourt

Tôi lo lắng về việc so sánh các lĩnh vực. Sự khác biệt trong các trường có thể không quan sát được từ quan điểm của khách hàng của đối tượng và vẫn có một so sánh sâu dựa trên các trường sẽ gắn cờ cho nó. Ngoài ra, việc so sánh các trường yêu cầu các đối tượng phải có cùng kiểu thời gian chạy có thể hạn chế.
beluchin

Để trả lời @beluchin ở trên, DeepEquals.deepEquals () không phải lúc nào cũng thực hiện so sánh từng trường. Đầu tiên, nó có một tùy chọn để sử dụng .equals () trên một phương thức nếu một phương thức tồn tại (đó không phải là phương thức trên Object) hoặc có thể bỏ qua nó. Thứ hai, khi so sánh Bản đồ / Bộ sưu tập, nó không nhìn vào Bộ sưu tập hoặc loại Bản đồ, cũng như các trường trên Bộ sưu tập / Bản đồ. Thay vào đó, nó so sánh chúng một cách logic. Một LinkedHashMap có thể bằng một TreeMap nếu chúng có cùng nội dung và các phần tử theo cùng một thứ tự. Đối với Bộ sưu tập và Bản đồ không theo thứ tự, chỉ các mục có kích thước và dấu bằng sâu là bắt buộc.
John DeRegnaucourt

khi so sánh Bản đồ / Bộ sưu tập, nó không nhìn vào Bộ sưu tập hoặc loại Bản đồ, cũng như các trường trên Bộ sưu tập / Bản đồ. Thay vào đó, nó so sánh chúng một cách hợp lý @JohnDeRegnaucourt, tôi sẽ tranh luận về sự so sánh hợp lý này tức là chỉ so sánh những gì publicnên áp dụng cho tất cả các loại chứ không chỉ áp dụng cho bộ sưu tập / bản đồ.
beluchin

10

Ghi đè Phương thức equals ()

Bạn chỉ cần ghi đè phương thức equals () của lớp bằng cách sử dụng EqualsBuilder.reflectionEquals () như được giải thích ở đây :

 public boolean equals(Object obj) {
   return EqualsBuilder.reflectionEquals(this, obj);
 }

7

Chỉ cần thực hiện so sánh hai phiên bản thực thể được sửa đổi bởi Hibernate Envers. Tôi bắt đầu viết khác biệt của riêng mình nhưng sau đó tìm thấy khuôn khổ sau.

https://github.com/SQiShER/java-object-diff

Bạn có thể so sánh hai đối tượng cùng loại và nó sẽ hiển thị các thay đổi, bổ sung và loại bỏ. Nếu không có thay đổi nào thì các vật bằng nhau (theo lý thuyết). Các chú thích được cung cấp cho các getters nên được bỏ qua trong quá trình kiểm tra. Công việc khung có các ứng dụng rộng hơn nhiều so với kiểm tra bình đẳng, tức là tôi đang sử dụng để tạo nhật ký thay đổi.

Hiệu suất của nó là ổn, khi so sánh các thực thể JPA, hãy đảm bảo tách chúng khỏi trình quản lý thực thể trước.


6

Tôi là usin XStream:

/**
 * @see java.lang.Object#equals(java.lang.Object)
 */
@Override
public boolean equals(Object o) {
    XStream xstream = new XStream();
    String oxml = xstream.toXML(o);
    String myxml = xstream.toXML(this);

    return myxml.equals(oxml);
}

/**
 * @see java.lang.Object#hashCode()
 */
@Override
public int hashCode() {
    XStream xstream = new XStream();
    String myxml = xstream.toXML(this);
    return myxml.hashCode();
}

5
Các tập hợp khác với danh sách có thể trả về các phần tử theo thứ tự khác nhau, do đó, so sánh chuỗi sẽ không thành công.
Alexey Berezkin

Ngoài ra, các lớp không thể tuần tự hóa sẽ không thành công
Zwelch

6

Trong AssertJ , bạn có thể làm:

Assertions.assertThat(expectedObject).isEqualToComparingFieldByFieldRecursively(actualObject);

Có thể nó sẽ không hoạt động trong mọi trường hợp, tuy nhiên nó sẽ hoạt động trong nhiều trường hợp hơn mà bạn nghĩ.

Đây là những gì tài liệu nói:

Khẳng định rằng đối tượng được kiểm tra (thực tế) bằng đối tượng đã cho dựa trên đệ quy một thuộc tính / trường bằng cách so sánh thuộc tính / trường (bao gồm cả những thuộc tính được kế thừa). Điều này có thể hữu ích nếu việc triển khai bằng trên thực tế không phù hợp với bạn. So sánh thuộc tính / trường đệ quy không được áp dụng trên các trường có triển khai bằng tùy chỉnh, tức là phương thức bằng được ghi đè sẽ được sử dụng thay vì so sánh trường theo trường.

So sánh đệ quy xử lý các chu kỳ. Theo mặc định, phao được so sánh với độ chính xác 1,0E-6 và gấp đôi với 1,0E-15.

Bạn có thể chỉ định bộ so sánh tùy chỉnh cho mỗi trường (lồng nhau) hoặc kiểu tương ứng bằng cách sử dụngComparatorForFields (Bộ so sánh, Chuỗi ...) và sử dụngComparatorForType (Bộ so sánh, Lớp).

Các đối tượng để so sánh có thể thuộc nhiều kiểu khác nhau nhưng phải có cùng thuộc tính / trường. Ví dụ: nếu đối tượng thực tế có trường Chuỗi tên, thì đối tượng khác cũng có một trường tên. Nếu một đối tượng có một trường và một thuộc tính trùng tên, thì giá trị thuộc tính sẽ được sử dụng trên trường.


1
isEqualToComparingFieldByFieldRecursivelyhiện không được dùng nữa. Sử dụng assertThat(expectedObject).usingRecursiveComparison().isEqualTo(actualObject);thay thế :)
dargmuesli

5

http://www.unitils.org/tutorial-reflectionassert.html

public class User {

    private long id;
    private String first;
    private String last;

    public User(long id, String first, String last) {
        this.id = id;
        this.first = first;
        this.last = last;
    }
}
User user1 = new User(1, "John", "Doe");
User user2 = new User(1, "John", "Doe");
assertReflectionEquals(user1, user2);

2
đặc biệt hữu ích nếu bạn phải xử lý các lớp đã tạo, nơi bạn không có bất kỳ ảnh hưởng nào về dấu bằng!
Matthias B

1
stackoverflow.com/a/1449051/829755 đã đề cập đến vấn đề này. bạn nên đã chỉnh sửa mà bài
user829755

1
@ user829755 Theo cách này, tôi mất điểm. VẬY tất cả về trò chơi điểm)) Mọi người thích nhận được tín dụng cho công việc hoàn thành, tôi cũng vậy.
gavenkoa

3

Hamcrest có cùng MatcherPropertyValuesAs . Nhưng nó dựa trên Công ước JavaBeans (sử dụng bộ định tuyến và bộ định tuyến). Nếu các đối tượng được so sánh không có getters và setters cho các thuộc tính của chúng, điều này sẽ không hoạt động.

import static org.hamcrest.beans.SamePropertyValuesAs.samePropertyValuesAs;
import static org.junit.Assert.assertThat;

import org.junit.Test;

public class UserTest {

    @Test
    public void asfd() {
        User user1 = new User(1, "John", "Doe");
        User user2 = new User(1, "John", "Doe");
        assertThat(user1, samePropertyValuesAs(user2)); // all good

        user2 = new User(1, "John", "Do");
        assertThat(user1, samePropertyValuesAs(user2)); // will fail
    }
}

Người dùng bean - với getters và setters

public class User {

    private long id;
    private String first;
    private String last;

    public User(long id, String first, String last) {
        this.id = id;
        this.first = first;
        this.last = last;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getFirst() {
        return first;
    }

    public void setFirst(String first) {
        this.first = first;
    }

    public String getLast() {
        return last;
    }

    public void setLast(String last) {
        this.last = last;
    }

}

Điều này hoạt động tốt cho đến khi bạn có POJO đang sử dụng isFoophương thức đọc cho thuộc Booleantính. Có một PR đã được mở từ năm 2016 để khắc phục nó. github.com/hamcrest/JavaHamcrest/pull/136
Snekse

2

Nếu các đối tượng của bạn triển khai Serializable, bạn có thể sử dụng điều này:

public static boolean deepCompare(Object o1, Object o2) {
    try {
        ByteArrayOutputStream baos1 = new ByteArrayOutputStream();
        ObjectOutputStream oos1 = new ObjectOutputStream(baos1);
        oos1.writeObject(o1);
        oos1.close();

        ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
        ObjectOutputStream oos2 = new ObjectOutputStream(baos2);
        oos2.writeObject(o2);
        oos2.close();

        return Arrays.equals(baos1.toByteArray(), baos2.toByteArray());
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

1

Ví dụ về Danh sách liên kết của bạn không khó xử lý. Khi mã đi ngang qua hai biểu đồ đối tượng, nó sẽ đặt các đối tượng đã truy cập vào Tập hợp hoặc Bản đồ. Trước khi chuyển sang tham chiếu đối tượng khác, tập hợp này được kiểm tra để xem đối tượng đã được duyệt chưa. Nếu vậy, không cần phải đi xa hơn.

Tôi đồng ý với người ở trên đã nói sử dụng LinkedList (giống như một Ngăn xếp nhưng không có các phương thức đồng bộ trên đó, vì vậy nó nhanh hơn). Duyệt qua biểu đồ đối tượng bằng cách sử dụng Ngăn xếp, trong khi sử dụng phản chiếu để lấy từng trường, là giải pháp lý tưởng. Được viết một lần, hashCode "bên ngoài" này () và "bên ngoài" hashCode () là cái mà tất cả các phương thức equals () và hashCode () nên gọi. Không bao giờ bạn cần đến phương thức equals () của khách hàng.

Tôi đã viết một đoạn mã duyệt qua một biểu đồ đối tượng hoàn chỉnh, được liệt kê trên Google Code. Xem json-io (http://code.google.com/p/json-io/). Nó tuần tự hóa một biểu đồ đối tượng Java thành JSON và giải mã hóa từ nó. Nó xử lý tất cả các đối tượng Java, có hoặc không có hàm tạo công khai, Có thể hóa nối tiếp hoặc không Có thể hóa nối tiếp, v.v. Chính mã truyền tải này sẽ là cơ sở cho việc triển khai "equals ()" và "hashcode ()" bên ngoài. Btw, JsonReader / JsonWriter (json-io) thường nhanh hơn ObjectInputStream / ObjectOutputStream được tích hợp sẵn.

JsonReader / JsonWriter này có thể được sử dụng để so sánh, nhưng nó sẽ không hữu ích với mã băm. Nếu bạn muốn có một mã băm chung () và bằng (), nó cần mã riêng. Tôi có thể thực hiện điều này với một khách truy cập đồ thị chung. Chúng ta sẽ thấy.

Các cân nhắc khác - trường tĩnh - điều đó thật dễ dàng - có thể bỏ qua chúng vì tất cả các trường hợp bằng () sẽ có cùng giá trị cho các trường tĩnh, vì trường tĩnh được chia sẻ trên tất cả các trường hợp.

Đối với các trường tạm thời - đó sẽ là một tùy chọn có thể lựa chọn. Đôi khi bạn có thể muốn những lần chuyển tiếp không tính những lần khác. "Đôi khi bạn cảm thấy giống như một hạt, đôi khi bạn không."

Kiểm tra lại dự án json-io (cho các dự án khác của tôi) và bạn sẽ tìm thấy dự án bên ngoài bằng () / hashcode (). Tôi chưa có tên cho nó, nhưng nó sẽ rõ ràng.


1

Apache cung cấp cho bạn một cái gì đó, chuyển đổi cả hai đối tượng thành chuỗi và so sánh các chuỗi, nhưng bạn phải Ghi đè toString ()

obj1.toString().equals(obj2.toString())

Ghi đè thành Chuỗi ()

Nếu tất cả các trường là kiểu nguyên thủy:

import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
@Override
public String toString() {return 
ReflectionToStringBuilder.toString(this);}

Nếu bạn có các trường và / hoặc bộ sưu tập và / hoặc bản đồ không phải nguyên thủy:

// Within class
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
@Override
public String toString() {return 
ReflectionToStringBuilder.toString(this,new 
MultipleRecursiveToStringStyle());}

// New class extended from Apache ToStringStyle
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.util.*;

public class MultipleRecursiveToStringStyle extends ToStringStyle {
private static final int    INFINITE_DEPTH  = -1;

private int                 maxDepth;

private int                 depth;

public MultipleRecursiveToStringStyle() {
    this(INFINITE_DEPTH);
}

public MultipleRecursiveToStringStyle(int maxDepth) {
    setUseShortClassName(true);
    setUseIdentityHashCode(false);

    this.maxDepth = maxDepth;
}

@Override
protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
    if (value.getClass().getName().startsWith("java.lang.")
            || (maxDepth != INFINITE_DEPTH && depth >= maxDepth)) {
        buffer.append(value);
    } else {
        depth++;
        buffer.append(ReflectionToStringBuilder.toString(value, this));
        depth--;
    }
}

@Override
protected void appendDetail(StringBuffer buffer, String fieldName, 
Collection<?> coll) {
    for(Object value: coll){
        if (value.getClass().getName().startsWith("java.lang.")
                || (maxDepth != INFINITE_DEPTH && depth >= maxDepth)) {
            buffer.append(value);
        } else {
            depth++;
            buffer.append(ReflectionToStringBuilder.toString(value, this));
            depth--;
        }
    }
}

@Override
protected void appendDetail(StringBuffer buffer, String fieldName, Map<?, ?> map) {
    for(Map.Entry<?,?> kvEntry: map.entrySet()){
        Object value = kvEntry.getKey();
        if (value.getClass().getName().startsWith("java.lang.")
                || (maxDepth != INFINITE_DEPTH && depth >= maxDepth)) {
            buffer.append(value);
        } else {
            depth++;
            buffer.append(ReflectionToStringBuilder.toString(value, this));
            depth--;
        }
        value = kvEntry.getValue();
        if (value.getClass().getName().startsWith("java.lang.")
                || (maxDepth != INFINITE_DEPTH && depth >= maxDepth)) {
            buffer.append(value);
        } else {
            depth++;
            buffer.append(ReflectionToStringBuilder.toString(value, this));
            depth--;
        }
    }
}}

0

Tôi đoán bạn biết điều này, nhưng về lý thuyết, bạn phải luôn ghi đè .equals để khẳng định rằng hai đối tượng thực sự bằng nhau. Điều này có nghĩa là họ kiểm tra các phương thức .equals bị ghi đè trên các thành viên của họ.

Loại điều này là lý do tại sao .equals được định nghĩa trong Object.

Nếu điều này được thực hiện một cách nhất quán, bạn sẽ không gặp vấn đề gì.


2
Vấn đề là tôi muốn tự động kiểm tra điều này cho một cơ sở mã lớn hiện có mà tôi đã không viết ... :)
Uri

0

Đảm bảo tạm dừng cho một so sánh sâu như vậy có thể là một vấn đề. Những gì sau đây nên làm gì? (Nếu bạn triển khai bộ so sánh như vậy, điều này sẽ tạo ra một bài kiểm tra đơn vị tốt.)

LinkedListNode a = new LinkedListNode();
a.next = a;
LinkedListNode b = new LinkedListNode();
b.next = b;

System.out.println(DeepCompare(a, b));

Đây là một cái khác:

LinkedListNode c = new LinkedListNode();
LinkedListNode d = new LinkedListNode();
c.next = d;
d.next = c;

System.out.println(DeepCompare(c, d));

Nếu bạn có câu hỏi mới, vui lòng đặt câu hỏi bằng cách nhấp vào nút Đặt câu hỏi . Bao gồm một liên kết đến câu hỏi này nếu nó giúp cung cấp ngữ cảnh.
YoungHobbit

@younghobbit: không, đây không phải là một câu hỏi mới. Dấu chấm hỏi trong câu trả lời không làm cho cờ đó thích hợp. Hãy chú ý hơn.
Ben Voigt

Từ này: Using an answer instead of a comment to get a longer limit and better formatting.Nếu đây là một nhận xét thì tại sao lại sử dụng phần trả lời? Đó là lý do tại sao tôi gắn cờ nó. không phải vì ?. Câu trả lời này đã được gắn cờ bởi một người khác, người đã không để lại bình luận phía sau. Tôi vừa nhận được cái này trong hàng đợi đánh giá. Có thể là tệ của tôi mà, tôi nên cẩn thận hơn.
YoungHobbit

0

Tôi nghĩ giải pháp dễ nhất được lấy cảm hứng từ giải pháp Ray Hulha là tuần tự hóa đối tượng và sau đó so sánh sâu với kết quả thô.

Việc tuần tự hóa có thể là byte, json, xml hoặc toString đơn giản, v.v. ToString có vẻ rẻ hơn. Lombok tạo ra chuỗi ToST có thể tùy chỉnh dễ dàng miễn phí cho chúng tôi. Xem ví dụ bên dưới.

@ToString @Getter @Setter
class foo{
    boolean foo1;
    String  foo2;        
    public boolean deepCompare(Object other) { //for cohesiveness
        return other != null && this.toString().equals(other.toString());
    }
}   

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.