Sự khác biệt giữa == và .equals trong Scala là gì?


144

Sự khác biệt giữa ==.equals()trong Scala, và khi nào nên sử dụng?

Việc triển khai có giống như trong Java không?

EDIT: Câu hỏi liên quan nói về các trường hợp cụ thể của AnyVal. Trường hợp tổng quát hơn là Any.



@Ben Tôi nghĩ rằng câu hỏi khác nên được đánh dấu là trùng lặp khi xem xét ngày được hỏi. Ngoài ra, tôi cảm thấy hai câu hỏi là khác nhau.
Jus12

Câu trả lời:


201

Bạn thường sử dụng ==, nó định tuyến đến equals, ngoại trừ việc nó xử lý nulls đúng cách. Tham chiếu bình đẳng (hiếm khi được sử dụng) là eq.


12
Nó cũng áp dụng khi sử dụng các thư viện Java?
Jus12

20
Nó làm. Ví dụ: java.util.ArrayList [Int] () == new java.util.ArrayList [Int] (), vì bằng với ArrayList là đẳng thức nội dung.
Didier Dupont

5
Ngoài ra còn có một số hành vi kỳ lạ xung quanh Int và Long và == so với .equals (). Cùng một số với Int và như Long trả về true cho == nhưng sai cho bằng. Vì vậy, == không phải lúc nào cũng định tuyến bằng.
Harold L

24
Thú vị hơn, cả hai 3 == BigInt(3)BigInt(3) == 3đều đúng. Nhưng, 3.equals(BigInt(3))là sai, trong khi đó BigInt(3).equals(3)là đúng. Do đó, thích sử dụng ==. Tránh sử dụng equals()trong scala. Tôi nghĩ rằng ==chuyển đổi ngầm định tốt, nhưng equals()không.
Naetmul

Vậy tại sao new java.lang.Integer(1) == new java.lang.Double(1.0)là đúng trong khi new java.lang.Integer(1) equals new java.lang.Double(1.0)là sai?
Eastsun

33

==là một phương thức cuối cùng và các cuộc gọi .equals, không phải là cuối cùng.

Điều này hoàn toàn khác với Java, trong đó ==là một toán tử chứ không phải là một phương thức và so sánh nghiêm ngặt sự bình đẳng tham chiếu cho các đối tượng.


29

TL; DR

  • equalsPhương pháp ghi đè để so sánh nội dung của từng trường hợp. Đây là equalsphương pháp tương tự được sử dụng trong Java
  • Sử dụng ==toán tử để so sánh, mà không phải lo lắng về nulltài liệu tham khảo
  • Sử dụng eqphương pháp để kiểm tra xem cả hai đối số có chính xác cùng một tham chiếu không. Không nên sử dụng trừ khi bạn hiểu cách thức hoạt động của nó và thường equalssẽ hoạt động cho những gì bạn cần thay thế. Và đảm bảo chỉ sử dụng điều này với các AnyRefđối số, không chỉAny

LƯU Ý: Trong trường hợp equals, giống như trong Java, nó có thể không trả về kết quả tương tự nếu bạn chuyển đổi các đối số, ví dụ như 1.equals(BigInt(1))sẽ trả về falsenơi nghịch đảo sẽ trả về true. Điều này là do mỗi thực hiện chỉ kiểm tra các loại cụ thể. Các số nguyên thủy không kiểm tra xem đối số thứ hai có Numberphải là BigIntloại hay không mà chỉ là các kiểu nguyên thủy khác

Chi tiết

Các AnyRef.equals(Any)phương pháp là một ghi đè bởi lớp con. Một phương thức từ Đặc tả Java cũng đã xuất hiện trên Scala. Nếu được sử dụng trên một cá thể chưa được đóng hộp, nó được đóng hộp để gọi cái này (mặc dù ẩn trong Scala; rõ ràng hơn trong Java với int-> Integer). Việc triển khai mặc định chỉ so sánh các tham chiếu (như trong Java)

Các Any.==(Any)phương pháp so sánh hai đối tượng và cho phép một trong hai tham số là null (như thể gọi một phương thức tĩnh với hai trường hợp). Nó so sánh nếu cả hai là null, sau đó nó gọi equals(Any)phương thức trên thể hiện được đóng hộp.

Các AnyRef.eq(AnyRef)phương pháp so sánh chỉ tài liệu tham khảo, đó là nơi mà các trường hợp nằm trong bộ nhớ. Không có quyền anh ngầm cho phương pháp này.

Ví dụ

  • 1 equals 2sẽ trở lại false, khi nó chuyển hướng đếnInteger.equals(...)
  • 1 == 2sẽ trở lại false, khi nó chuyển hướng đếnInteger.equals(...)
  • 1 eq 2 sẽ không biên dịch, vì nó yêu cầu cả hai đối số phải là kiểu AnyRef
  • new ArrayList() equals new ArrayList()sẽ trở lại true, vì nó kiểm tra nội dung
  • new ArrayList() == new ArrayList()sẽ trở lại true, khi nó chuyển hướng đếnequals(...)
  • new ArrayList() eq new ArrayList()sẽ trở lại false, vì cả hai đối số là các trường hợp khác nhau
  • foo equals foosẽ trở lại true, trừ trường hợp foonull, sau đó sẽ ném mộtNullPointerException
  • foo == foosẽ trở lại true, ngay cả khi foonull
  • foo eq foosẽ trả về true, vì cả hai đối số liên kết đến cùng một tham chiếu

6

Có một sự khác biệt thú vị giữa ==equalscho FloatDoublecác loại: Chúng đối xử NaNkhác nhau:

scala> Double.NaN == Double.NaN
res3: Boolean = false

scala> Double.NaN equals Double.NaN
res4: Boolean = true

Chỉnh sửa: Như đã được chỉ ra trong một nhận xét - "điều này cũng xảy ra trong Java" - phụ thuộc vào chính xác đây là gì:

public static void main(final String... args) {
    final double unboxedNaN = Double.NaN;
    final Double boxedNaN = Double.valueOf(Double.NaN);

    System.out.println(unboxedNaN == unboxedNaN);
    System.out.println(boxedNaN == boxedNaN);
    System.out.println(boxedNaN.equals(boxedNaN));
}

Cái này sẽ in

false
true
true

Vì vậy, unboxedNannăng suất falsekhi được so sánh bằng nhau vì đây là cách các số dấu phẩy động của IEEE định nghĩa nó và điều này thực sự sẽ xảy ra trong mọi ngôn ngữ lập trình (mặc dù nó bằng cách nào đó làm rối tung khái niệm nhận dạng).

NaN được đóng hộp mang lại kết quả đúng cho việc so sánh sử dụng ==trong Java khi chúng ta so sánh các tham chiếu đối tượng.

Tôi không có lời giải thích cho equalstrường hợp này, IMHO nó thực sự nên hoạt động giống như ==trên các giá trị kép không được đóng hộp, nhưng thực tế thì không.

Dịch sang Scala vấn đề phức tạp hơn một chút vì Scala đã hợp nhất các loại đối tượng và nguyên thủy thành Anyvà dịch thành đôi nguyên thủy và Double đóng hộp khi cần. Do đó, scala ==rõ ràng tập trung vào việc so sánh các NaNgiá trị nguyên thủy nhưng equalssử dụng giá trị được xác định trên các giá trị Double được đóng hộp (có rất nhiều phép thuật chuyển đổi ngầm đang diễn ra và có những thứ được đưa lên gấp đôi RichDouble).

Nếu bạn thực sự cần phải tìm hiểu nếu một cái gì đó thực sự được NaNsử dụng isNaN:


và điều này cũng xảy ra trong Java!
Iwan Satria

4

Trong Scala == trước tiên hãy kiểm tra các giá trị Null và sau đó gọi phương thức bằng trên đối tượng đầu tiê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.