Bagaimana kelas String menimpa operator +?

129

Mengapa di Jawa Anda dapat menambahkan Strings dengan operator +, ketika String adalah kelas? Dalam String.javakode saya tidak menemukan implementasi untuk operator ini. Apakah konsep ini melanggar orientasi objek?

Pooya
sumber
21
Operator Plus ( +) adalah fitur bahasa Java.
adatapost
18
Ini semua sihir kompiler. Anda tidak dapat melakukan overloading operator di Jawa.
Singa
18
Saya pikir yang aneh adalah bahwa String diimplementasikan sebagai perpustakaan di luar bahasa (di java.lang.String), namun ia memiliki dukungan khusus di dalam bahasa. Itu tidak benar.
13ren
@ 13ren Anda salah. String adalah bagian dari kelas java.lang yang merupakan singkatan dari bahasa - java lanaguage !!! Semua kelas dalam paket ini spesial. Perhatikan Anda tidak perlu mengimpor java.lang untuk menggunakannya.

Jawaban:

156

Mari kita lihat ekspresi sederhana berikut di Jawa

int x=15;
String temp="x = "+x;

Compiler mengubahnya "x = "+x;menjadi StringBuilderinternal dan digunakan .append(int)untuk "menambahkan" integer ke string.

5.1.11. Konversi string

Jenis apa pun dapat dikonversi untuk mengetikkan String dengan konversi string.

Nilai x tipe T primitif pertama kali dikonversi ke nilai referensi seolah-olah dengan memberikannya sebagai argumen untuk ekspresi pembuatan instance kelas yang sesuai (§15.9):

  • Jika T adalah boolean, maka gunakan Boolean baru (x).
  • Jika T adalah karakter, maka gunakan Karakter baru (x).
  • Jika T adalah byte, pendek, atau int, maka gunakan Integer baru (x).
  • Jika T panjang, maka gunakan Long baru (x).
  • Jika T adalah float, maka gunakan Float baru (x).
  • Jika T ganda, maka gunakan Double baru (x).

Nilai referensi ini kemudian dikonversi ke tipe String oleh konversi string.

Sekarang hanya nilai referensi yang perlu dipertimbangkan:

  • Jika referensi adalah nol, itu dikonversi ke string "null" (empat karakter ASCII n, u, l, l).
  • Jika tidak, konversi dilakukan seolah-olah dengan doa metode toString objek yang direferensikan tanpa argumen; tetapi jika hasil dari memanggil metode toString adalah null, maka string "null" digunakan sebagai gantinya.

Metode toString didefinisikan oleh Object kelas primordial (§4.3.2). Banyak kelas menimpanya, terutama Boolean, Character, Integer, Long, Float, Double, dan String.

Lihat §5.4 untuk detail konteks konversi string.

15.18.1.

Optimalisasi Penggabungan String: Suatu implementasi dapat memilih untuk melakukan konversi dan penggabungan dalam satu langkah untuk menghindari pembuatan dan kemudian membuang objek String perantara. Untuk meningkatkan kinerja penggabungan string berulang, kompiler Java dapat menggunakan kelas StringBuffer atau teknik serupa untuk mengurangi jumlah objek String menengah yang dibuat dengan mengevaluasi ekspresi.

Untuk tipe primitif, implementasi juga dapat mengoptimalkan pembuatan objek wrapper dengan mengonversi langsung dari tipe primitif ke string.

Versi yang dioptimalkan tidak akan benar-benar melakukan konversi String terbungkus penuh terlebih dahulu.

Ini adalah ilustrasi yang bagus dari versi yang dioptimalkan yang digunakan oleh kompiler, meskipun tanpa konversi yang primitif, di mana Anda dapat melihat kompiler mengubah hal-hal menjadi StringBuilder di latar belakang:

http://caprazzi.net/posts/java-bytecode-string-concatenation-and-stringbuilder/


Kode java ini:

public static void main(String[] args) {
    String cip = "cip";
    String ciop = "ciop";
    String plus = cip + ciop;
    String build = new StringBuilder(cip).append(ciop).toString();
}

Hasilkan ini - lihat bagaimana kedua gaya gabungan mengarah ke bytecode yang sama:

 L0
    LINENUMBER 23 L0
    LDC "cip"
    ASTORE 1
   L1
    LINENUMBER 24 L1
    LDC "ciop"
    ASTORE 2

   // cip + ciop

   L2
    LINENUMBER 25 L2

    NEW java/lang/StringBuilder
    DUP
    ALOAD 1
    INVOKESTATIC java/lang/String.valueOf(Ljava/lang/Object;)Ljava/lang/String;
    INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V
    ALOAD 2
    INVOKEVIRTUAL java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String;

    ASTORE 3

    // new StringBuilder(cip).append(ciop).toString()

   L3
    LINENUMBER 26 L3

    NEW java/lang/StringBuilder
    DUP
    ALOAD 1
    INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V
    ALOAD 2
    INVOKEVIRTUAL java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String;

    ASTORE 4
   L4
    LINENUMBER 27 L4
    RETURN

Melihat contoh di atas dan bagaimana kode byte berdasarkan kode sumber dalam contoh yang diberikan dihasilkan, Anda akan dapat melihat bahwa kompiler secara internal mengubah pernyataan berikut

cip+ciop; 

ke

new StringBuilder(cip).append(ciop).toString();

Dengan kata lain, operator +dalam rangkaian string secara efektif merupakan singkatan untuk StringBuilderidiom yang lebih verbose .

Singa
sumber
3
Terima kasih banyak, saya tidak terbiasa dengan kode byte jvm tetapi menghasilkan kode untuk String plus = cip + ciop; dan String build = new StringBuilder (cip) .append (ciop) .toString (); sama. dan pertanyaan saya adalah apakah operasi ini melanggar orientasi objek?
Pooya
7
Tidak, tidak. Overloading operator (seperti dalam C ++ dan beberapa bahasa) memiliki beberapa kelemahan dan desainer Java merasa itu konsep yang agak membingungkan dan dihilangkan dari Jawa. Bagi saya, bahasa berorientasi objek harus memiliki konsep utama pewarisan, polimorfisme, dan enkapsulasi yang dimiliki Java.
Singa
2
ya, tapi saya pikir operator ini kelebihan beban untuk kelas String
Pooya
3
Ya, overloading operator digunakan di Jawa untuk penggabungan tipe String, namun Anda tidak dapat menentukan operator Anda sendiri (seperti dalam C ++, C # dan beberapa bahasa lainnya).
Singa
5
@Pooya: sebenarnya "int / int" vs. "int / float" sudah kelebihan operator, bahkan C pun memilikinya. Namun yang tidak dimiliki oleh C (dan Java) adalah overloading operator yang ditentukan pengguna: satu-satunya hal yang menentukan cara berbeda yang dapat digunakan operator (dalam C dan Java) adalah definisi bahasa (dan perbedaannya diterapkan dalam penyusun). C ++ berbeda karena memungkinkan overloading operator yang ditentukan pengguna (yang sering disebut sebagai "overloading operator").
Joachim Sauer
27

Ini adalah fitur kompiler Java yang memeriksa operan +operator. Dan berdasarkan operan itu menghasilkan kode byte:

  • Untuk String, ini menghasilkan kode ke string concat
  • Untuk Angka, ini menghasilkan kode untuk menambahkan angka.

Inilah yang dikatakan oleh Java spec :

Operator + dan -disebut operator aditif. AdditiveExpression: MultiplicativeExpression AdditiveExpression + MultiplicativeExpression AdditiveExpression - MultiplicativeExpression

Operator aditif memiliki prioritas yang sama dan secara asosiatif kiri asosiatif (mereka dikelompokkan dari kiri ke kanan). Jika jenis operan salah satu +operator adalah String, maka operasinya adalah penggabungan string.

Kalau tidak, tipe dari masing-masing operan +operator haruslah tipe yang dapat dikonversi (§5.1.8) menjadi tipe numerik primitif, atau kesalahan waktu kompilasi terjadi.

Dalam setiap kasus, tipe dari masing-masing operan dari -operator biner harus merupakan tipe yang dapat dikonversi (§5.1.8) menjadi tipe numerik primitif, atau kesalahan waktu kompilasi terjadi.

Ramesh PVK
sumber
7
Kutipan dari spec sama sekali tidak relevan dengan pertanyaan ini.
Ernest Friedman-Hill
Ini adalah ekstrak "Jika jenis operan dari operator + adalah String, maka operasinya adalah penggabungan string. Jika tidak, tipe dari masing-masing operan dari operator + harus merupakan tipe yang dapat dikonversi (§5.1.8 ) ke tipe numerik primitif, atau kesalahan waktu kompilasi terjadi ". Bisakah Anda memberi tahu saya mengapa itu tidak relevan?
Ramesh PVK
7
Itu tidak mengatakan apa-apa tentang bagaimana itu diterapkan, yang merupakan pertanyaan. Saya pikir poster sudah mengerti bahwa fitur itu ada.
Ernest Friedman-Hill
14

Bagaimana kelas String menimpa + operator?

Tidak. Compiler melakukannya. Sebenarnya, kompiler membebani operator + berlebih untuk operan String.

Marquis dari Lorne
sumber
6

Pertama-tama (+) kelebihan beban tidak diganti

Bahasa Java menyediakan dukungan khusus untuk operator rangkaian string (+), yang telah kelebihan beban untuk objek Java Strings.

  1. Jika operan sisi kiri adalah String, ia berfungsi sebagai penggabungan.

  2. Jika operan sisi kiri Integer berfungsi sebagai operator tambahan

Pramod Kumar
sumber
3
(2) Jika operan kiri adalah bilangan bulat maka itu akan dibongkar secara otomatis ke intdan kemudian aturan normal Jawa berlaku.
Marquis of Lorne
2
Dua aturan yang diberikan di bawah kutipan salah: Saya percaya mereka seharusnya: dua primitif (atau kelas unboxable) = penambahan; setidaknya satu string = rangkaian
mwfearnley
4

Bahasa Java menyediakan dukungan khusus untuk operator rangkaian string (+) dan untuk konversi objek lain ke string. Rangkaian string diimplementasikan melalui StringBuilder(atau StringBuffer) kelas dan appendmetodenya.

Jigar Pandya
sumber
4

Arti +operator ketika diterapkan Stringdidefinisikan oleh bahasa, karena semua orang sudah menulis. Karena Anda tampaknya tidak menemukan ini cukup meyakinkan, pertimbangkan ini:

Ints, float dan doubles semuanya memiliki representasi biner yang berbeda, dan karenanya menambahkan dua int adalah operasi yang berbeda, dalam hal manipulasi bit, daripada menambahkan dua float: Untuk int Anda dapat menambahkan sedikit demi sedikit, membawa sedikit dan memeriksa luapan; untuk pelampung Anda harus berurusan dengan mantra dan eksponen secara terpisah.

Jadi, pada prinsipnya, "penambahan" tergantung pada sifat dari objek yang "ditambahkan". Java mendefinisikannya untuk String dan juga int dan float (longs, doubles, ...)

Alexis
sumber
3

The +Operator biasanya diganti dengan StringBuilderpada waktu kompilasi. Periksa jawaban ini untuk detail lebih lanjut tentang masalah ini.

Scorpio
sumber
Jika ini masalahnya, apakah ada alasan mengapa StringBuilder ada untuk penggunaan umum? Apakah ada kasus di mana +operator tidak diganti oleh StringBuilder?
Cemre
2
Pertanyaan yang ingin Anda tanyakan adalah "mengapa operator + ada sama sekali untuk penggunaan umum?", Karena itulah kekejian di sini. Adapun pertanyaan Anda yang lain, saya tidak tahu persis, tapi saya kira tidak ada kasus seperti itu.
Scorpio
Compiler mungkin menggunakan concat () sebagai gantinya, jika hanya ada dua elemen. Compiler juga gagal untuk mengganti concat () dengan StringBuilder (atau menggunakan beberapa StringBuilders dan menambahkannya bersama-sama) ketika programmer membangun string dalam kode bersarang / loop panjang - menggunakan tunggal, StringBuilder eksplisit akan lebih baik untuk kinerja.
user158037