Mengapa operator Java + =, - =, * =, / = compound tidak memerlukan casting?

3633

Sampai hari ini, saya pikir itu misalnya:

i += j;

Itu hanya jalan pintas untuk:

i = i + j;

Tetapi jika kita coba ini:

int i = 5;
long j = 8;

Maka i = i + j;tidak akan dikompilasi tetapi i += j;akan dikompilasi dengan baik.

Apakah ini berarti bahwa sebenarnya i += j;merupakan jalan pintas untuk hal seperti ini i = (type of i) (i + j)?

Honza Brabec
sumber
135
Saya kaget Java mengizinkan ini, menjadi bahasa yang lebih keras dari pendahulunya. Kesalahan dalam casting dapat menyebabkan kegagalan kritis, seperti halnya dengan Ariane5 Penerbangan 501 di mana pelampung 64-bit dilemparkan ke bilangan bulat 16-bit mengakibatkan kecelakaan.
SQLDiver
103
Dalam sistem kontrol penerbangan yang ditulis dalam Java, ini akan menjadi yang paling tidak membuat Anda khawatir @SQLDiver
Ross Drew
10
Sebenarnya i+=(long)j;bahkan akan dikompilasi dengan baik.
Tharindu Sathischandra
6
Dorongan konstan oleh satu set pengembang untuk akurasi dan yang lain untuk kemudahan penggunaan benar-benar menarik. Kami hampir membutuhkan dua versi bahasa, satu yang sangat tepat dan satu yang mudah digunakan. Mendorong Jawa dari kedua arah bergerak ke arah yang tidak cocok untuk kedua kelompok.
Bill K
5
jika memang membutuhkan casting, di mana Anda akan meletakkannya? i += (int) f;melakukan f sebelum penambahan, jadi itu tidak setara. (int) i += f;melemparkan hasil setelah penugasan, tidak sama. tidak akan ada tempat untuk memasang pemain yang menandakan bahwa Anda ingin memberikan nilai setelah menambahkan, tetapi sebelum penugasan.
Norill Tempest

Jawaban:

2441

Seperti biasa dengan pertanyaan-pertanyaan ini, JLS memegang jawabannya. Dalam hal ini §15.26.2 Operator Penugasan Majemuk . Ekstrak:

Ekspresi penugasan majemuk bentuk E1 op= E2setara dengan E1 = (T)((E1) op (E2)), di mana Tjenis E1, kecuali yang E1dievaluasi hanya sekali.

Contoh yang dikutip dari §15.26.2

[...] kode berikut ini benar:

short x = 3;
x += 4.6;

dan menghasilkan x yang memiliki nilai 7 karena setara dengan:

short x = 3;
x = (short)(x + 4.6);

Dengan kata lain, asumsi Anda benar.

Lukas Eder
sumber
42
Jadi i+=jkompilasi ketika saya memeriksa diri saya sendiri, tetapi itu akan mengakibatkan hilangnya presisi bukan? Jika itu masalahnya, mengapa itu tidak memungkinkan terjadi di i = i + j juga? Mengapa mengganggu kami di sana?
bad_keypoints
46
@ronnieaka: Saya menduga bahwa perancang bahasa merasa bahwa dalam satu kasus ( i += j), lebih aman untuk mengasumsikan bahwa kehilangan presisi diinginkan dibandingkan dengan kasus lainnya ( i = i + j)
Lukas Eder
12
Tidak, tepat di sana, di depan saya! Maaf saya tidak menyadarinya sebelumnya. Seperti dalam jawaban Anda E1 op= E2 is equivalent to E1 = (T)((E1) op (E2)),, jadi itu semacam typecasting tersirat seperti (turun dari panjang ke int). Sedangkan di i = i + j, kita harus melakukannya secara eksplisit, yaitu, menyediakan (T)bagian dalam E1 = ((E1) op (E2))bukan?
bad_keypoints
11
Kemungkinan alasan mengapa kompiler Java menambahkan typecast adalah karena jika Anda mencoba melakukan aritmatika pada tipe yang tidak kompatibel, tidak ada cara melakukan typecast dari hasil menggunakan formulir yang dikontrak. Sebuah typecast dari hasil umumnya lebih akurat daripada typecast dari argumen yang bermasalah. Tidak ada typecast yang akan membuat kontraksi menjadi tidak berguna ketika menggunakan tipe yang tidak kompatibel, karena akan selalu menyebabkan kompiler mengeluarkan kesalahan.
ThePyroEagle
6
Itu tidak bulat. Itu dilemparkan (= terpotong)
Lukas Eder
483

Contoh yang baik dari casting ini menggunakan * = atau / =

byte b = 10;
b *= 5.7;
System.out.println(b); // prints 57

atau

byte b = 100;
b /= 2.5;
System.out.println(b); // prints 40

atau

char ch = '0';
ch *= 1.1;
System.out.println(ch); // prints '4'

atau

char ch = 'A';
ch *= 1.5;
System.out.println(ch); // prints 'a'
Peter Lawrey
sumber
11
@AkshatAgarwal ch adalah char. 65 * 1.5 = 97.5 -> Mengerti?
Sajal Dutta
79
Ya, tapi saya bisa melihat beberapa pemula datang ke sini, membaca ini, dan pergi berpikir bahwa Anda dapat mengubah karakter apa pun dari huruf besar ke huruf kecil dengan mengalikannya dengan 1,5.
Dawood ibn Kareem
103
@ Davidvidallall Setiap karakter selama A;)
Peter Lawrey
14
@PeterLawrey & @DavidWallace Saya akan mengungkapkan rahasia Anda - ch += 32 = D
Minhas Kamal
256

Pertanyaan yang sangat bagus The Java Bahasa spesifikasi menegaskan saran Anda.

Misalnya, kode berikut ini benar:

short x = 3;
x += 4.6;

dan menghasilkan x yang memiliki nilai 7 karena setara dengan:

short x = 3;
x = (short)(x + 4.6);
Thirler
sumber
15
Atau lebih menyenangkan: "int x = 33333333; x + = 1.0f;".
supercat
5
@ supercat, tipuan apa ini? Konversi pelebaran yang salah bulat, diikuti oleh penambahan yang tidak benar-benar mengubah hasil, casting ke int lagi untuk menghasilkan hasil yang paling tak terduga untuk pikiran manusia normal.
neXus
1
@neXus: IMHO, aturan konversi seharusnya diperlakukan double->floatsebagai pelebaran, atas dasar bahwa nilai-nilai tipe floatmengidentifikasi bilangan real kurang spesifik daripada yang bertipe double. Jika seseorang memandang doublesebagai alamat pos yang lengkap dan floatsebagai kode pos 5 digit, dimungkinkan untuk memenuhi permintaan untuk kode pos yang diberikan alamat lengkap, tetapi tidak mungkin untuk secara akurat menentukan permintaan untuk alamat lengkap yang hanya diberikan kode pos . Mengonversi alamat jalan ke kode pos adalah operasi yang merugi, tapi ...
supercat
1
... seseorang yang membutuhkan alamat lengkap umumnya tidak akan meminta hanya kode pos. Konversi dari float->doublesama dengan mengkonversi kode pos AS 90210 dengan "Kantor Pos AS, Beverly Hills CA 90210".
supercat
181

Iya,

pada dasarnya ketika kita menulis

i += l; 

kompiler mengonversi ini menjadi

i = (int)(i + l);

Saya baru saja memeriksa .classkode file.

Benar-benar hal yang baik untuk diketahui

Umesh Awasthi
sumber
3
Bisakah Anda memberi tahu saya classfile mana ini?
nanofarad
6
@hexafraction: apa yang Anda maksud dengan file kelas? jika Anda bertanya tentang file kelas yang saya sebutkan di posting saya daripada itu adalah versi yang sesuai dari kelas java Anda
Umesh Awasthi
3
Oh, Anda menyebutkan "file" kode file kelas, yang membuat saya percaya classfile tertentu terlibat. Saya mengerti maksud Anda sekarang.
nanofarad
@ Bogdan Itu seharusnya tidak menjadi masalah dengan font yang digunakan dengan benar. Seorang programmer yang memilih font yang salah untuk pemrograman harus dengan jelas berpikir tentang bagaimana melanjutkan ...
glglgl
7
@glglgl Saya tidak setuju bahwa seseorang harus bergantung pada font untuk membedakan dalam kasus-kasus itu ... tetapi semua orang memiliki kebebasan untuk memilih apa yang dianggap terbaik.
Bogdan Alexandru
92

Anda perlu melakukan cast dari longke int explicitlydalam kasus i = i + l maka akan mengkompilasi dan memberikan output yang benar. Suka

i = i + (int)l;

atau

i = (int)((long)i + l); // this is what happens in case of += , dont need (long) casting since upper casting is done implicitly.

tetapi dalam kasus +=itu hanya berfungsi dengan baik karena operator secara implisit melakukan pengecoran tipe dari jenis variabel kanan ke jenis variabel kiri sehingga tidak perlu dilemparkan secara eksplisit.

dku.rajkumar
sumber
7
Dalam hal ini, "pemeran implisit" bisa jadi lossy. Pada kenyataannya, sebagai negara @LukasEder dalam jawabannya, para pemain untuk intdilakukan setelah para +. Compiler akan (harus?) Memberikan peringatan jika ia benar-benar melemparkan longke int.
Romain
63

Masalahnya di sini adalah tipe casting.

Saat Anda menambahkan int dan panjang,

  1. Objek int dicor ke panjang & keduanya ditambahkan dan Anda mendapatkan objek panjang.
  2. tetapi objek panjang tidak dapat secara implisit dicor ke int. Jadi, Anda harus melakukannya secara eksplisit.

Tetapi +=dikodekan sedemikian rupa sehingga tidak mengetik casting.i=(int)(i+m)

Dinesh Sachdev 108
sumber
54

Di Java, konversi jenis dilakukan secara otomatis ketika jenis ekspresi di sisi kanan operasi penugasan dapat dipromosikan dengan aman ke jenis variabel di sisi kiri penugasan. Dengan demikian kami dapat menetapkan:

 byte -> short -> int -> long -> float -> double. 

Hal yang sama tidak akan bekerja sebaliknya. Sebagai contoh, kita tidak dapat secara otomatis mengkonversi panjang ke int karena yang pertama membutuhkan lebih banyak penyimpanan daripada yang kedua dan akibatnya informasi mungkin hilang. Untuk memaksa konversi semacam itu, kita harus melakukan konversi eksplisit.
Jenis - Konversi

tinker_fairy
sumber
2
Hei, tapi long2 kali lebih besar dari float.
Nama Tampilan
11
A floattidak dapat menampung setiap intnilai yang mungkin , dan a doubletidak dapat menyimpan setiap longnilai yang mungkin .
Alex MDC
2
Apa yang Anda maksud dengan "dikonversi dengan aman"? Dari bagian terakhir dari jawaban saya dapat menyimpulkan bahwa yang Anda maksud konversi otomatis (pemain implisit) yang tentu saja tidak benar dalam kasus float -> long. float pi = 3.14f; panjang b = pi; akan menghasilkan kesalahan kompiler.
Lukas
1
Anda akan lebih baik membedakan tipe primitif floating point dengan tipe primitif integer. Mereka bukan hal yang sama.
ThePyroEagle
Java memiliki aturan konversi sederhana yang mengharuskan penggunaan gips dalam banyak pola di mana perilaku tanpa gips akan sesuai dengan harapan, tetapi tidak memerlukan gips dalam banyak pola yang biasanya keliru. Sebagai contoh, kompiler akan menerima double d=33333333+1.0f;tanpa keluhan, meskipun hasilnya 33333332.0 kemungkinan tidak akan seperti yang dimaksudkan (kebetulan, jawaban 33333334.0f yang benar secara hitung akan diwakili sebagai salah satu floatatau int).
supercat
46

Terkadang, pertanyaan seperti itu bisa ditanyakan pada wawancara.

Misalnya, ketika Anda menulis:

int a = 2;
long b = 3;
a = a + b;

tidak ada typecasting otomatis. Dalam C ++ tidak akan ada kesalahan saat mengkompilasi kode di atas, tetapi di Jawa Anda akan mendapatkan sesuatu seperti Incompatible type exception.

Jadi untuk menghindarinya, Anda harus menulis kode Anda seperti ini:

int a = 2;
long b = 3;
a += b;// No compilation error or any exception due to the auto typecasting
Stopfan
sumber
6
Terima kasih atas wawasan mengenai perbandingan oppenggunaan di C ++ dengan penggunaannya di Jawa. Saya selalu suka melihat hal-hal sepele ini dan saya pikir mereka menyumbangkan sesuatu untuk percakapan yang mungkin sering diabaikan.
Thomas
2
Namun pertanyaan itu sendiri adalah menarik, bertanya ini dalam sebuah wawancara bodoh. Itu tidak membuktikan bahwa orang tersebut dapat menghasilkan kode kualitas yang baik - itu hanya membuktikan bahwa ia memiliki cukup kesabaran untuk mempersiapkan ujian sertifikat Oracle. Dan "menghindari" jenis yang tidak kompatibel dengan menggunakan konversi otomatis yang berbahaya dan dengan demikian menyembunyikan kemungkinan kesalahan melimpah, bahkan mungkin membuktikan bahwa orang tersebut tidak dapat menghasilkan kemungkinan kode kualitas yang baik. Sialan penulis Java untuk semua konversi otomatis dan tinju otomatis ini dan semuanya!
Honza Zidek
25

Perbedaan utama adalah bahwa dengan a = a + b, tidak ada typecasting yang sedang berlangsung, sehingga kompiler marah kepada Anda karena tidak typecasting. Tapi dengan a += b, apa yang sebenarnya dilakukannya adalah typecasting bke tipe yang kompatibel dengannya a. Jadi, jika Anda melakukannya

int a=5;
long b=10;
a+=b;
System.out.println(a);

Apa yang sebenarnya Anda lakukan adalah:

int a=5;
long b=10;
a=a+(int)b;
System.out.println(a);
takra
sumber
5
Operator penugasan majemuk melakukan konversi penyempitan dari hasil operasi biner, bukan operan kanan. Jadi, dalam contoh Anda, 'a + = b' tidak setara dengan 'a = a + (int) b' tetapi, seperti yang dijelaskan oleh jawaban lain di sini, untuk 'a = (int) (a + b)'.
Lew Bloch
13

Titik halus di sini ...

Ada typecast implisit untuk i+jkapan jganda dan iint. Java SELALU mengubah integer menjadi dobel ketika ada operasi di antara mereka.

Untuk memperjelas di i+=jmana ibilangan bulat dan jganda dapat digambarkan sebagai

i = <int>(<double>i + j)

Lihat: deskripsi tentang casting implisit ini

Anda mungkin ingin typecast jke (int)dalam hal ini untuk kejelasan.

Gabe Nones
sumber
1
Saya pikir kasus yang lebih menarik mungkin int someInt = 16777217; float someFloat = 0.0f; someInt += someFloat;. Menambahkan nol someInttidak seharusnya memengaruhi nilainya, tetapi mempromosikan someIntke floatdapat mengubah nilainya.
supercat
5

Spesifikasi Bahasa Jawa mendefinisikan E1 op= E2setara dengan di E1 = (T) ((E1) op (E2))mana Tadalah jenis E1dan E1dievaluasi sekali .

Itu jawaban teknis, tetapi Anda mungkin bertanya-tanya mengapa itu sebuah kasus. Nah, mari kita simak program berikut ini.

public class PlusEquals {
    public static void main(String[] args) {
        byte a = 1;
        byte b = 2;
        a = a + b;
        System.out.println(a);
    }
}

Apa yang dicetak oleh program ini?

Apakah Anda menebak 3? Sayang sekali, program ini tidak dapat dikompilasi. Mengapa? Nah, kebetulan bahwa penambahan byte di Jawa didefinisikan untuk mengembalikan sebuahint . Ini, saya percaya itu karena Java Virtual Machine tidak mendefinisikan operasi byte untuk menghemat bytecodes (bagaimanapun, ada sejumlah yang terbatas), menggunakan operasi integer sebagai gantinya adalah detail implementasi yang diekspos dalam bahasa.

Tetapi jika a = a + btidak bekerja, itu berarti a += btidak akan pernah bekerja untuk byte jika itu E1 += E2didefinisikan E1 = E1 + E2. Seperti yang ditunjukkan contoh sebelumnya, itu memang benar. Sebagai peretasan untuk membuat +=operator bekerja untuk byte dan celana pendek, ada pemeran yang terlibat. Ini bukan retasan yang hebat, tetapi pada saat Java 1.0 bekerja, fokusnya adalah pada pelepasan bahasa untuk memulai. Sekarang, karena kompatibilitas ke belakang, peretasan ini diperkenalkan di Java 1.0 tidak dapat dihapus.

Konrad Borowski
sumber