Setiap programmer Java yang kompeten tahu bahwa Anda perlu menggunakan String.equals () untuk membandingkan string, bukan == karena == memeriksa persamaan referensi.
Ketika saya berurusan dengan string, sebagian besar waktu saya memeriksa kesetaraan nilai daripada kesetaraan referensi. Tampaknya bagi saya bahwa akan lebih intuitif jika bahasa memungkinkan nilai string dibandingkan dengan hanya menggunakan ==.
Sebagai perbandingan, operator C # == memeriksa kesetaraan nilai untuk string . Dan jika Anda benar-benar perlu memeriksa persamaan referensi, Anda dapat menggunakan String.ReferenceEquals.
Poin penting lainnya adalah bahwa String tidak dapat diubah, sehingga tidak ada salahnya dilakukan dengan mengizinkan fitur ini.
Apakah ada alasan khusus mengapa ini tidak diterapkan di Jawa?
==
objek kesetaraan daneq
referensi kesetaraan ( ofps.oreilly.com/titles/9780596155957/… ).==
operator hanya memetakanEquals
jika==
operator dilaksanakan dengan cara itu. Perilaku default untuk==
adalah sama denganReferenceEquals
(sebenarnya,ReferenceEquals
didefinisikan sebagai versi objek==
)Jawaban:
Saya kira itu hanya konsistensi, atau "prinsip paling tidak heran". String adalah objek, sehingga akan mengejutkan jika diperlakukan berbeda dari objek lain.
Pada saat Java keluar (~ 1995), hanya memiliki sesuatu seperti
String
adalah kemewahan total bagi sebagian besar programmer yang terbiasa merepresentasikan string sebagai array yang diakhiri dengan null.String
Perilaku sekarang adalah apa yang dulu, dan itu bagus; mengubah perilaku secara halus di kemudian hari dapat memiliki efek yang mengejutkan dan tidak diinginkan dalam program kerja.Sebagai catatan tambahan, Anda dapat menggunakan
String.intern()
untuk mendapatkan representasi canonical (diinternir) dari string, setelah itu perbandingan dapat dibuat==
. Magang membutuhkan waktu, tetapi setelah itu, perbandingan akan sangat cepat.Tambahan: tidak seperti beberapa jawaban yang disarankan, ini bukan tentang mendukung kelebihan operator . The
+
operator (concatenation) bekerja padaString
s meskipun Java tidak mendukung operator overloading; itu hanya ditangani sebagai kasus khusus di kompiler, diselesaikanStringBuilder.append()
. Demikian pula,==
bisa ditangani sebagai kasus khusus.Lalu mengapa heran dengan kasus khusus
+
tetapi tidak dengan==
? Karena,+
tidak dikompilasi ketika diterapkan pada non-String
objek sehingga cepat terlihat. Perbedaan perilaku dari==
akan jauh lebih jelas dan dengan demikian jauh lebih mencengangkan ketika hits Anda.sumber
James Gosling , pencipta Jawa, menjelaskannya pada Juli 2000 :
sumber
==
operator kelebihan beban untuk objek dan primitif. The+
operator kelebihan beban untukbyte
,short
,int
,long
,float
,double
,String
dan mungkin beberapa orang lain yang saya lupa. Itu akan sangat mungkin untuk kelebihan==
untukString
juga.Konsistensi dalam bahasa. Memiliki operator yang bertindak berbeda dapat mengejutkan bagi programmer. Java tidak memungkinkan pengguna untuk membebani operator - karena itu referensi kesetaraan adalah satu-satunya makna yang masuk akal untuk
==
antar objek.Di dalam jawa:
==
bandingkan persamaan numerik==
bandingkan kesetaraan boolean==
bandingkan identitas referensi.equals(Object o)
untuk membandingkan nilaiItu dia. Aturan sederhana dan mudah untuk mengidentifikasi apa yang Anda inginkan. Ini semua tercakup dalam bagian 15.21 JLS . Ini terdiri dari tiga subbagian yang mudah dipahami, diterapkan, dan dipikirkan.
Setelah Anda mengizinkan overloading
==
, perilaku yang tepat bukanlah sesuatu yang bisa Anda lihat di JLS dan letakkan jari Anda pada item tertentu dan katakan "begitulah cara kerjanya," kodenya bisa menjadi sulit untuk dipikirkan. Perilaku tepat==
mungkin mengejutkan bagi pengguna. Setiap kali Anda melihatnya, Anda harus kembali dan memeriksa untuk melihat apa artinya sebenarnya.Karena Java tidak memungkinkan untuk overloading operator, orang perlu cara untuk memiliki tes kesetaraan nilai yang Anda dapat menimpa definisi dasar. Jadi, itu dimandatkan oleh pilihan desain ini.
==
di Java menguji numerik untuk tipe numerik, kesetaraan boolean untuk tipe boolean, dan referensi kesetaraan untuk yang lainnya (yang dapat menggantikan.equals(Object o)
untuk melakukan apa pun yang mereka inginkan untuk kesetaraan nilai).Ini bukan masalah "apakah ada kasus penggunaan untuk konsekuensi tertentu dari keputusan desain ini" melainkan "ini adalah keputusan desain untuk memfasilitasi hal-hal lain ini, ini adalah konsekuensi dari itu."
String magang , adalah salah satu contohnya. Menurut JLS 3.10.5 , semua string literal diinternir. String lain diinternir jika seseorang memintanya
.intern()
. Itu"foo" == "foo"
benar adalah konsekuensi dari keputusan desain yang dibuat untuk meminimalkan jejak memori yang diambil oleh String literal. Di luar itu, String interning adalah sesuatu yang ada di level JVM yang memiliki sedikit eksposur kepada pengguna, tetapi dalam sebagian besar kasus, seharusnya tidak menjadi sesuatu yang menyangkut programmer (dan menggunakan case untuk programmer tidak sesuatu yang tinggi dalam daftar untuk para desainer ketika mempertimbangkan fitur ini).Orang-orang akan menunjukkan itu
+
dan+=
kelebihan beban untuk String. Namun, itu tidak penting. Tetap menjadi kasus bahwa jika==
memiliki makna kesetaraan nilai untuk String (dan hanya String), orang akan membutuhkan metode yang berbeda (yang hanya ada dalam String) untuk referensi kesetaraan. Selanjutnya, ini akan metode sia-sia menyulitkan yang mengambil Obyek dan berharap==
untuk berperilaku salah satu cara dan.equals()
berperilaku lain pengguna yang membutuhkan untuk kasus khusus semua orang metode untuk String.Kontrak yang konsisten untuk
==
pada Objects adalah bahwa itu hanya referensi kesetaraan dan yang.equals(Object o)
ada untuk semua objek yang harus menguji kesetaraan nilai. Rumitnya hal ini merumitkan terlalu banyak hal.sumber
new String("foo") == new String("foo")
mungkin benar (lihat Deduplication String ).Java tidak mendukung overloading operator, yang berarti
==
hanya berlaku untuk tipe atau referensi primitif. Yang lainnya membutuhkan doa metode. Mengapa perancang melakukan ini adalah pertanyaan yang hanya bisa mereka jawab. Jika saya harus menebak, itu mungkin karena kelebihan operator membawa kerumitan yang mereka tidak ingin tambahkan.Saya bukan ahli dalam C #, tetapi perancang bahasa itu tampaknya telah mengaturnya sehingga setiap primitif adalah
struct
dan setiapstruct
objek. Karena C # memungkinkan overloading operator, pengaturan itu membuatnya sangat mudah untuk semua kelas, tidak hanyaString
, untuk membuatnya bekerja dengan cara yang "diharapkan" dengan operator mana pun. C ++ memungkinkan hal yang sama.sumber
==
dimaksudkan kesetaraan string, kita akan memerlukan notasi lain untuk kesetaraan referensi.==
. Itu secara efektif menambah kelebihan operator, yang akan memiliki implikasi luar biasa pada cara Java diimplementasikan.ClassName.ReferenceEquals(a,b)
), dan==
operator danEquals
metode default keduanya menunjuk keReferenceEquals
.Ini telah dibuat berbeda dalam bahasa lain.
Dalam Object Pascal (Delphi / Free Pascal) dan C #, operator persamaan didefinisikan untuk membandingkan nilai, bukan referensi, ketika beroperasi pada string.
Khususnya di Pascal, string adalah tipe primitif (salah satu hal yang sangat saya sukai tentang Pascal, mendapatkan NullreferenceException hanya karena string yang tidak diinisialisasi hanya menjengkelkan) dan memiliki semantik copy-on-write sehingga membuat (sebagian besar) operasi string sangat murah (dengan kata lain, hanya terlihat setelah Anda mulai merangkai string multi-megabyte).
Jadi, ini adalah keputusan desain bahasa untuk Java. Ketika mereka mendesain bahasa mereka mengikuti cara C ++ (seperti Std :: String) sehingga string adalah objek, yang merupakan peretasan IMHO untuk mengkompensasi C yang tidak memiliki tipe string nyata, alih-alih menjadikan string sebagai primitif (yang mana mereka).
Jadi untuk alasan mengapa, saya hanya bisa berspekulasi mereka membuatnya mudah di pihak mereka dan tidak mengkode operator membuat pengecualian pada compiler ke string.
sumber
String
seharusnya menjadi tipe primitif di Jawa. Tidak seperti tipe lain, kompiler perlu tahu tentangString
; lebih lanjut, operasi pada itu akan cukup umum bahwa untuk banyak jenis aplikasi mereka dapat menimbulkan hambatan kinerja (yang dapat dikurangi dengan dukungan asli).string
[Huruf kecil] tipikal akan memiliki objek yang dialokasikan pada heap untuk menampung isinya, tetapi tidak ada referensi "normal" untuk objek itu yang ada di mana saja; dengan demikian bisa menjadi satu-diarahkanChar[]
atauByte[]
daripada harus menjadi diarahkanChar[]
melalui objek lain.Di Jawa, tidak ada operator kelebihan beban apa pun, dan itulah sebabnya operator pembanding hanya kelebihan beban untuk tipe primitif.
Kelas 'String' bukan primitif, sehingga tidak memiliki overloading untuk '==' dan menggunakan default untuk membandingkan alamat objek dalam memori komputer.
Saya tidak yakin, tapi saya berpikir bahwa di Jawa 7 atau 8 oracle membuat pengecualian dalam compiler untuk mengenali
str1 == str2
sebagaistr1.equals(str2)
sumber
s1
dans2
dan memberi mereka konten yang sama, mereka melewatis1.equals(s2)
perbandingan kesetaraan ( ) tetapi bukan perbandingan referensi yang sama (==
) karena mereka dua objek yang berbeda. Mengubah semantik==
menjadi kesetaraan akan menyebabkans1 == s2
untuk mengevaluasi ditrue
mana ia digunakan untuk mengevaluasifalse
.Java tampaknya telah dirancang untuk menegakkan aturan mendasar bahwa
==
operator harus sah setiap kali satu operan dapat dikonversi ke jenis yang lain, dan harus membandingkan hasil konversi tersebut dengan operan yang tidak dikonversi.Aturan ini hampir tidak unik untuk Java, tetapi memiliki beberapa efek jangkauan (dan IMHO sial) pada desain aspek-aspek terkait bahasa lainnya. Akan lebih bersih untuk menentukan perilaku
==
sehubungan dengan kombinasi tertentu dari tipe operan, dan melarang kombinasi tipe X dan Y di manax1==y1
danx2==y1
tidak akan menyiratkanx1==x2
, tetapi bahasa jarang melakukan itu [di bawah filosofi itu,double1 == long1
harus menunjukkan apakahdouble1
bukan representasi yang tepat darilong1
, atau menolak untuk dikompilasi;int1==Integer1
harus dilarang, tetapi harus ada cara non-lempar yang nyaman dan efisien untuk menguji apakah suatu objek bilangan bulat kotak dengan nilai tertentu (perbandingan dengan sesuatu yang bukan bilangan bulat kotak seharusnya dikembalikanfalse
)].Sehubungan dengan menerapkan
==
operator ke string, jika Jawa telah melarang perbandingan langsung antara operan tipeString
danObject
, itu bisa menghindari kejutan dalam perilaku==
, tetapi tidak ada perilaku yang bisa diterapkan untuk perbandingan seperti itu yang tidak akan mengejutkan. Memiliki dua string string yang disimpan dalam tipeObject
berperilaku berbeda dari referensi yang disimpan dalam tipeString
akan jauh lebih mencengangkan daripada memiliki salah satu perilaku tersebut berbeda dari perbandingan tipe campuran legal. JikaString1==Object1
legal, itu akan menyiratkan bahwa satu-satunya cara untuk perilakuString1==String2
danObject1==Object2
untuk mencocokkanString1==Object1
adalah bagi mereka untuk saling mencocokkan.sumber
==
pada objek hanya harus memanggil (null-safe) sama dengan dan sesuatu yang lain (misalnya,===
atauSystem.identityEqual
) harus digunakan untuk perbandingan identitas. Mencampur primitif dan objek pada awalnya dilarang (tidak ada autobox sebelum 1,5) dan kemudian beberapa aturan sederhana dapat ditemukan (mis. Null-safe unbox, lalu cast, lalu bandingkan).int==Integer
operatorfalse
jikaInteger
nol, dan jika tidak membandingkan nilai-nilai, pendekatan itu tidak seperti perilaku==
dalam semua keadaan lain, di mana ia tanpa syarat memaksa kedua operan untuk jenis yang sama sebelum membandingkan mereka. Secara pribadi saya bertanya-tanya apakah auto-unboxing diberlakukan sebagai upaya untuk memungkinkanint==Integer
untuk memiliki perilaku yang tidak masuk akal ...int
dan melakukan perbandingan referensi akan konyol [tetapi tidak selalu gagal]. Kalau tidak, saya tidak melihat alasan untuk mengizinkan konversi implisit yang dapat gagal dengan NPE.==
tidak ada hubungannya denganidentityEquals
. +++ "operator kesetaraan terpisah untuk kesetaraan nilai dan referensi" - tetapi yang mana? Saya akan mempertimbangkan baik primitif==
maupunequals
sebagai melakukan perbandingan nilai dalam arti yangequals
terlihat pada nilai referensi. +++ Ketika==
dimaksudequals
, makaint==Integer
HARUS melakukan autoboxing dan membandingkan referensi menggunakan null-safe sama dengan. +++ Aku takut, ideku bukan milikku, tapi apa yang Kotlin lakukan.==
tidak pernah menguji kesetaraan referensi, maka secara masuk akal dapat melakukan uji kesetaraan nilai-aman-nol. Fakta bahwa itu tidak kesetaraan referensi tes, bagaimanapun, sangat membatasi bagaimana dapat menangani perbandingan referensi / nilai campuran tanpa inkonsistensi. Perhatikan juga bahwa Java ditetapkan pada anggapan bahwa operator mempromosikan kedua operan ke tipe yang sama, daripada menghasilkan perilaku khusus berdasarkan kombinasi tipe yang terlibat. Sebagai contoh,16777217==16777216.0f
kembalitrue
karena melakukan konversi lossy dari operan pertama kefloat
, sementara ...Secara umum, ada alasan yang sangat bagus untuk dapat menguji apakah dua referensi objek menunjuk ke objek yang sama. Saya sudah banyak kali menulis
Saya mungkin atau mungkin tidak memiliki fungsi yang sama dalam kasus tersebut. Jika saya lakukan, fungsi equals dapat membandingkan seluruh konten dari kedua objek. Seringkali itu hanya membandingkan beberapa pengidentifikasi. "A dan B adalah referensi ke objek yang sama" dan "A dan B adalah dua objek berbeda dengan konten yang sama", tentu saja, dua ide yang sangat berbeda.
Mungkin benar bahwa untuk objek yang tidak dapat diubah, seperti Strings, ini bukan masalah. Dengan objek yang tidak berubah, kita cenderung menganggap objek dan nilainya sebagai hal yang sama. Nah, ketika saya mengatakan "kita", maksud saya "saya", setidaknya.
Tentu saja itu kembali salah, tetapi saya dapat melihat seseorang berpikir itu harus benar.
Tetapi begitu Anda mengatakan bahwa == membandingkan pegangan referensi dan bukan konten untuk Objek secara umum, membuat kasus khusus untuk Strings akan berpotensi membingungkan. Seperti yang dikatakan orang lain di sini, bagaimana jika Anda ingin membandingkan gagang dua objek String? Apakah akan ada fungsi khusus untuk melakukannya hanya untuk Strings?
Dan bagaimana dengan ...
Apakah itu salah karena mereka adalah dua objek yang berbeda, atau benar karena mereka adalah string yang isinya sama?
Jadi ya, saya mengerti bagaimana programmer bingung dengan ini. Saya sudah melakukannya sendiri, maksud saya menulis jika myString == "foo" ketika saya maksud jika myString.equals ("foo"). Tapi pendek mendesain ulang arti operator == untuk semua objek, saya tidak melihat bagaimana mengatasinya.
sumber
==
untuk berarti "string yang sama".==
, seperti yang Anda sebutkan di bagian akhir.==
kesalahan cenderung.Ini adalah pertanyaan yang valid untuk
Strings
, dan tidak hanya untuk string, tetapi juga untuk Objects berubah lainnya yang mewakili beberapa "nilai", misalnyaDouble
,BigInteger
dan bahkanInetAddress
.Untuk membuat
==
operator dapat digunakan dengan String dan kelas nilai lainnya, saya melihat tiga alternatif:Suruh kompiler mengetahui tentang semua kelas nilai ini dan cara membandingkan isinya. Jika itu hanya beberapa kelas dari
java.lang
paket, saya akan mempertimbangkan itu, tetapi itu tidak mencakup kasus-kasus seperti InetAddress.Izinkan operator overloading sehingga kelas mendefinisikan
==
perilaku perbandingannya.Hapus konstruktor publik dan punya metode statis yang mengembalikan instance dari kumpulan, selalu mengembalikan instance yang sama untuk nilai yang sama. Untuk menghindari kebocoran memori, Anda memerlukan sesuatu seperti SoftReferences di pool, yang tidak ada di Java 1.0. Dan sekarang, untuk menjaga kompatibilitas,
String()
konstruktor tidak dapat dihapus lagi.Satu-satunya hal yang masih bisa dilakukan hari ini adalah memperkenalkan kelebihan operator, dan secara pribadi saya tidak akan suka Java untuk pergi rute itu.
Bagi saya, keterbacaan kode adalah yang paling penting, dan seorang programmer Java tahu bahwa operator memiliki makna tetap, didefinisikan dalam spesifikasi bahasa, sedangkan metode didefinisikan oleh beberapa kode, dan artinya harus dilihat dalam Javadoc metode. Saya ingin tetap dengan perbedaan itu bahkan jika itu berarti bahwa perbandingan String tidak akan dapat menggunakan
==
operator.Hanya ada satu aspek dari perbandingan Java yang mengganggu saya: efek auto-boxing dan -unboxing. Ini menyembunyikan perbedaan antara tipe primitif dan tipe pembungkus. Tetapi ketika Anda membandingkannya dengan
==
, mereka SANGAT berbeda.sumber