Phương thức assertEquals của Java có đáng tin cậy không?


199

Tôi biết rằng ==có một số vấn đề khi so sánh hai Strings. Có vẻ như đó String.equals()là một cách tiếp cận tốt hơn. Chà, tôi đang làm thử nghiệm JUnit và thiên hướng của tôi là sử dụng assertEquals(str1, str2). Đây có phải là cách đáng tin cậy để khẳng định hai Chuỗi chứa cùng một nội dung không? Tôi sẽ sử dụng assertTrue(str1.equals(str2)), nhưng sau đó bạn không nhận được lợi ích khi thấy những giá trị thực tế và dự kiến ​​sẽ thất bại.

Trên một lưu ý liên quan, có ai có một liên kết đến một trang hoặc chủ đề giải thích rõ ràng các vấn đề với str1 == str2?


1
Nếu bạn không chắc chắn, bạn có thể đọc mã hoặc Javadoc. BTW nếu bạn muốn kiểm tra chúng là cùng một đối tượng bạn có thể sử dụng assertSame.
Peter Lawrey

2
Nếu str1 và str2 là null, assertEquals () là true, nhưng assertTrue (str1.equals (str2)) sẽ ném ngoại lệ. Ví dụ đầu tiên cũng sẽ in một thông báo lỗi hữu ích như nội dung của str1 và str2, thứ hai thì không.
Peter Lawrey

Câu trả lời:


274

Bạn nên luôn luôn sử dụng .equals()khi so sánh Stringstrong Java.

JUnit gọi .equals()phương thức để xác định đẳng thức trong phương thứcassertEquals(Object o1, Object o2) .

Vì vậy, bạn chắc chắn an toàn khi sử dụng assertEquals(string1, string2). (Vì Strings là Objects)

Đây là một liên kết đến một câu hỏi Stackoverflow tuyệt vời liên quan đến một số khác biệt giữa ==.equals().


12
IIRC assertEquals () thành công nếu cả hai chuỗi đều null. Nếu đây không phải là điều bạn muốn thì hãy gọi assertNotNull ().
finnw

10
ngoài ra, nếu bạn muốn kiểm tra ==, bạn có thể gọi assertSame ()
james

7
Tôi sẽ không nói luôn ; đôi khi bình đẳng tham chiếu là mong muốn, ngay cả đối với chuỗi.
Karu

30

assertEqualssử dụng equalsphương pháp để so sánh. Có một khẳng định khác nhau assertSame, trong đó sử dụng ==toán tử.

Để hiểu lý do tại sao ==không nên sử dụng với chuỗi, bạn cần hiểu những gì ==: nó kiểm tra danh tính. Đó là, a == bkiểm tra xem abtham chiếu đến cùng một đối tượng . Nó được tích hợp vào ngôn ngữ và hành vi của nó không thể bị thay đổi bởi các lớp khác nhau. Các equalsphương pháp, mặt khác, có thể ghi đè bởi các lớp học. Mặc dù hành vi mặc định của nó (trong Objectlớp) là thực hiện kiểm tra danh tính bằng cách sử dụng ==toán tử, nhiều lớp, bao gồm String, ghi đè lên nó để thay vào đó thực hiện kiểm tra "tương đương". Trong trường hợp String, thay vì kiểm tra nếu kiểm tra xem các đối tượng mà chúng tham chiếu có phải là cả hai chuỗi chứa chính xác các ký tự không.abtham chiếu đến cùng một đối tượng,a.equals(b)

Thời gian tương tự: hãy tưởng tượng rằng mỗi Stringđối tượng là một mảnh giấy với một cái gì đó được viết trên đó. Giả sử tôi có hai mảnh giấy có chữ "Foo" được viết trên đó và một mảnh khác có chữ "Bar" được viết trên đó. Nếu tôi lấy hai mảnh giấy đầu tiên và sử dụng ==để so sánh chúng, nó sẽ trả về falsevì về cơ bản nó hỏi "đây có phải là cùng một mảnh giấy không?". Nó thậm chí không cần nhìn vào những gì được viết trên giấy. Thực tế là tôi đang đưa cho nó hai mảnh giấy (chứ không phải cùng một hai lần) có nghĩa là nó sẽ trở lại false. equalsTuy nhiên, nếu tôi sử dụng , equalsphương thức sẽ đọc hai mẩu giấy và thấy rằng chúng nói cùng một điều ("Foo"), và vì vậy nó sẽ trở lại true.

Điều gây nhầm lẫn với Chuỗi là Java có khái niệm Chuỗi "nội bộ" và điều này (thực sự) được thực hiện tự động trên bất kỳ chuỗi ký tự nào trong mã của bạn. Điều này có nghĩa là nếu bạn có hai chuỗi ký tự chuỗi tương đương trong mã của mình (ngay cả khi chúng thuộc các lớp khác nhau) thì cả hai sẽ thực sự đề cập đến cùng một Stringđối tượng. Điều này làm cho ==toán tử trở lại truethường xuyên hơn người ta có thể mong đợi.


"Nghĩa là, a == b kiểm tra xem a và b có phải là cùng một đối tượng không." Về mặt kỹ thuật, nó kiểm tra xem a và b THAM KHẢO cho cùng một đối tượng, vì a và b là các tham chiếu. Trừ khi tôi rất sai.
bob

@ user1903064 đúng vậy. Do các biến không nguyên thủy chỉ có thể chứa các tham chiếu trong Java, nên việc bỏ qua mức độ gián tiếp thêm khi nói về chúng là điều phổ biến, nhưng tôi đồng ý rằng trong trường hợp này rõ ràng hơn là có lợi. Tôi đã cập nhật câu trả lời. Cám ơn vì sự gợi ý!
Laurence Gonsalves

7

Tóm lại - bạn có thể có hai đối tượng String chứa cùng một ký tự nhưng là các đối tượng khác nhau (ở các vị trí bộ nhớ khác nhau). Toán tử == kiểm tra xem hai tham chiếu đang trỏ đến cùng một đối tượng (vị trí bộ nhớ), nhưng phương thức equals () kiểm tra xem các ký tự có giống nhau không.

Thông thường, bạn quan tâm đến việc kiểm tra xem hai Chuỗi có chứa các ký tự giống nhau không, liệu chúng có trỏ đến cùng một vị trí bộ nhớ hay không.


4
public class StringEqualityTest extends TestCase {
    public void testEquality() throws Exception {
        String a = "abcde";
        String b = new String(a);
        assertTrue(a.equals(b));
        assertFalse(a == b);
        assertEquals(a, b);
    }
}

3

Có, nó được sử dụng tất cả thời gian để thử nghiệm. Rất có khả năng khung thử nghiệm sử dụng .equals () để so sánh như thế này.

Dưới đây là một liên kết giải thích "lỗi bình đẳng chuỗi". Về cơ bản, các chuỗi trong Java là các đối tượng và khi bạn so sánh sự bình đẳng của đối tượng, thông thường chúng được so sánh dựa trên địa chỉ bộ nhớ chứ không phải theo nội dung. Do đó, hai chuỗi sẽ không chiếm cùng một địa chỉ, ngay cả khi nội dung của chúng giống hệt nhau, vì vậy chúng sẽ không khớp chính xác, mặc dù chúng trông giống nhau khi được in.

http://blog.enrii.com/2006/03/15/java-opes-equality-common-mistake/


3

JUnit assertEquals(obj1, obj2)thực sự gọi obj1.equals(obj2).

Ngoài ra còn assertSame(obj1, obj2)obj1 == obj2(nghĩa là xác minh điều đó obj1obj2đang tham chiếu cùng một ví dụ), đó là điều bạn đang cố gắng tránh.

Vậy là bạn ổn.


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.