Apa perbedaan antara == dan .equals di Scala?

144

Apa perbedaan antara ==dan .equals()dalam Scala, dan kapan menggunakannya?

Apakah implementasinya sama dengan di Jawa?

EDIT: Pertanyaan terkait berbicara tentang kasus spesifik AnyVal. Kasus yang lebih umum adalah Any.

Jus12
sumber
@Ben Saya pikir pertanyaan lain harus ditandai sebagai duplikat mengingat tanggal yang diajukan. Saya juga merasa kedua pertanyaan itu berbeda.
Jus12

Jawaban:

201

Anda biasanya menggunakan ==, itu rute ke equals, kecuali itu memperlakukan nulldengan benar. Referensi kesetaraan (jarang digunakan) adalah eq.

Didier Dupont
sumber
12
Apakah itu juga berlaku saat menggunakan perpustakaan Java?
Jus12
20
Itu benar. Misalnya java.util.ArrayList [Int] () == new java.util.ArrayList [Int] () baru, sama dengan yang ada di ArrayList adalah kesetaraan konten.
Didier Dupont
5
Ada juga beberapa perilaku aneh di sekitar Int dan Long dan == versus .equals (). Angka yang sama dengan Int dan Long mengembalikan true untuk == tetapi false untuk equals. Jadi == tidak selalu merutekan ke sama.
Harold L
24
Lebih menarik, Keduanya 3 == BigInt(3)dan BigInt(3) == 3itu benar. Tapi, 3.equals(BigInt(3))itu salah, padahal BigInt(3).equals(3)itu benar. Karena itu, lebih suka menggunakan ==. Hindari penggunaan equals()dalam skala. Saya pikir ==melakukan konversi tersirat dengan baik, tetapi equals()tidak.
Naetmul
Jadi mengapa new java.lang.Integer(1) == new java.lang.Double(1.0)benar sementara new java.lang.Integer(1) equals new java.lang.Double(1.0)itu salah?
Eastsun
34

==adalah metode terakhir, dan panggilan .equals, yang tidak final.

Ini sangat berbeda dari Jawa, di mana ==operator daripada metode dan secara ketat membandingkan kesetaraan referensi untuk objek.

Don Roby
sumber
29

TL; DR

  • Ganti equalsmetode untuk membandingkan konten setiap instance. Ini adalah equalsmetode yang sama yang digunakan di Jawa
  • Gunakan ==operator untuk membandingkan, tanpa khawatir tentang nullreferensi
  • Gunakan eqmetode untuk memeriksa apakah kedua argumen tersebut PERSIS referensi yang sama. Dianjurkan untuk tidak menggunakan kecuali Anda mengerti bagaimana ini bekerja dan sering equalsakan bekerja untuk apa yang Anda butuhkan. Dan pastikan untuk hanya menggunakan ini dengan AnyRefargumen, bukan hanyaAny

CATATAN: Pada kasus equals, seperti halnya di Jawa, ini mungkin tidak mengembalikan hasil yang sama jika Anda mengganti argumen mis. 1.equals(BigInt(1))Akan mengembalikan falsetempat invers akan kembali true. Ini karena setiap implementasi hanya memeriksa jenis tertentu. Bilangan primitif tidak memeriksa apakah argumen kedua adalah tipe Numberatau bukan BigInttetapi hanya tipe primitif lainnya

Detail

The AnyRef.equals(Any)Metode adalah salah satu ditimpa oleh subclass. Metode dari Spesifikasi Java yang telah datang ke Scala juga. Jika digunakan pada instance unboxed, itu kotak untuk memanggil ini (meskipun tersembunyi di Scala; lebih jelas di Jawa dengan int-> Integer). Implementasi default hanya membandingkan referensi (seperti di Jawa)

The Any.==(Any)Metode membandingkan dua benda dan memungkinkan argumen baik untuk menjadi nol (seolah-olah memanggil metode statis dengan dua contoh). Ini membandingkan jika keduanya null, maka itu memanggil equals(Any)metode pada contoh kotak.

The AnyRef.eq(AnyRef)Metode membandingkan hanya referensi, yang mana contoh terletak di memori. Tidak ada tinju tersirat untuk metode ini.

Contohnya

  • 1 equals 2akan kembali false, karena dialihkan keInteger.equals(...)
  • 1 == 2akan kembali false, karena dialihkan keInteger.equals(...)
  • 1 eq 2 tidak akan dikompilasi, karena kedua argumen harus bertipe AnyRef
  • new ArrayList() equals new ArrayList()akan kembali true, karena memeriksa konten
  • new ArrayList() == new ArrayList()akan kembali true, karena dialihkan keequals(...)
  • new ArrayList() eq new ArrayList()akan kembali false, karena kedua argumen adalah contoh yang berbeda
  • foo equals fooakan kembali true, kecuali fooadalah null, maka akan melemparNullPointerException
  • foo == fooakan kembali true, bahkan jika fooadanull
  • foo eq fooakan kembali true, karena kedua argumen terhubung ke referensi yang sama
zjohn4
sumber
6

Ada perbedaan yang menarik antara ==dan equalsuntuk Floatdan Doublejenis: Mereka memperlakukan secara NaNberbeda:

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

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

Sunting: Seperti yang ditunjukkan dalam komentar - "ini juga terjadi di Jawa" - tergantung pada apa tepatnya ini :

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

Ini akan dicetak

false
true
true

Jadi, unboxedNanhasilnya falseketika dibandingkan untuk kesetaraan karena ini adalah bagaimana angka-angka floating point IEEE mendefinisikannya dan ini harus benar-benar terjadi di setiap bahasa pemrograman (meskipun entah bagaimana mengacaukan gagasan identitas).

Kotak NaN menghasilkan true untuk perbandingan menggunakan ==di Jawa karena kami membandingkan referensi objek.

Saya tidak punya penjelasan untuk equalskasus ini, IMHO itu benar-benar harus berperilaku sama seperti ==pada nilai ganda tanpa kotak, tetapi tidak.

Diterjemahkan ke Scala masalah ini sedikit lebih rumit karena Scala telah menyatukan jenis objek primitif dan menjadi Anydan menerjemahkan ke dalam primitif ganda dan kotak ganda yang diperlukan. Dengan demikian scala ==tampaknya bermuara pada perbandingan NaNnilai-nilai primitif tetapi equalsmenggunakan yang didefinisikan pada nilai ganda kotak (ada banyak sihir konversi implisit yang terjadi dan ada hal-hal yang di gandakan menjadi ganda oleh RichDouble).

Jika Anda benar-benar perlu mencari tahu apakah sesuatu benar-benar NaNdigunakan isNaN:

scravy
sumber
dan ini juga terjadi di Jawa!
Iwan Satria
4

Dalam Scala == pertama periksa nilai Null dan kemudian memanggil metode sama dengan objek pertama

mendongkrak
sumber