Neden Java'nın + =, - =, * =, / = bileşik atama işleçleri döküm gerektirmez?


3633

Bugüne kadar şöyle düşündüm:

i += j;

Sadece bir kısaydı:

i = i + j;

Ama bunu denersek:

int i = 5;
long j = 8;

O i = i + j;zaman derlenmeyecek ama i += j;iyi derlenecektir.

Aslında i += j;böyle bir şey için bir kısayol olduğu anlamına mı geliyor i = (type of i) (i + j)?


135
Java'nın öncekilerden daha katı bir dil olmasına izin verdiğine şaşırdım. Dökümdeki hatalar, 16 bitlik bir tam sayıya dökülen 64 bitlik bir float çökme ile sonuçlanan Ariane5 Flight 501'de olduğu gibi kritik bir hataya neden olabilir.
SQLDiver

103
Java ile yazılmış bir uçuş kontrol sisteminde, bu endişeleriniz en az olacaktır @SQLDiver
Ross Drew

10
Aslında i+=(long)j;iyi derlenecek.
Tharindu Sathischandra

6
Bir grup geliştiricinin doğruluk için bir diğeri kullanım kolaylığı için sürekli itmesi gerçekten ilginç. Neredeyse iki dil versiyonuna ihtiyacımız var, biri inanılmaz derecede hassas ve kullanımı kolay bir versiyon. Java'yı her iki yönden de itmek, onu her iki grup için de uygun olmaya doğru hareket ettirir.
Bill K

5
Döküm gerektiriyorsa, nereye koyardınız? i += (int) f;eklemeden önce f çevirir, bu yüzden eşdeğer değildir. (int) i += f;sonucu atamadan sonra yayınlar, eşdeğeri değil. ekledikten sonra, ancak atamadan önce değeri yayınlamak istediğinizi belirten bir yayın koymak için yer olmazdı.
Norill Tempest

Yanıtlar:


2441

Her zaman olduğu gibi, bu sorularda JLS cevabı tutar. Bu durumda §15.26.2 Bileşik Atama Operatörleri . Bir özü:

Şeklinde olan bir bileşik atama ifade E1 op= E2eşdeğerdir E1 = (T)((E1) op (E2)), burada Ttürüdür E1dışında, E1sadece bir kere elde edildi.

§15.26.2'den alıntı yapılan bir örnek

[...] aşağıdaki kod doğrudur:

short x = 3;
x += 4.6;

ve x'in 7 değerine sahip olmasıyla sonuçlanır, çünkü şuna eşdeğerdir:

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

Başka bir deyişle, varsayımınız doğrudur.


42
Yani i+=jderlemesinin kendim kontrol olarak, ancak hassas hakkının kaybına neden olur? Bu durumda, neden i = i + j'de olmasına izin vermiyor? Neden bizi rahatsız ediyorsun?
bad_keypoints

46
@ronnieaka: Dil tasarımcılarının bir durumda ( i += j), diğer durumun aksine hassasiyet kaybının arzu edildiğini varsaymak daha güvenli olduğunu tahmin ediyorum ( i = i + j)
Lukas Eder

12
Hayır, tam orada, önümde! Üzgünüm, daha önce fark etmedim. Cevabınızda olduğu gibi E1 op= E2 is equivalent to E1 = (T)((E1) op (E2)), bu da dolaylı aşağı tipleme (uzuntan int'e) gibi. İ i + j = Oysa, biz, açıkça bunu yani sağlamak zorunda (T)yer E1 = ((E1) op (E2))İş'in değil mi?
bad_keypoints

11
Java derleyicisinin bir typecast eklemesinin olası bir nedeni, uyumsuz türlerde aritmetik yapmaya çalışıyorsanız, sözleşmeli formu kullanarak sonucun bir typecast gerçekleştirmenin bir yolu olmamasıdır. Sonucun tipik tahmini, genellikle sorunlu argümanın tipik tahmininden daha doğrudur. Herhangi bir yazım hatası, derleyici her zaman bir hata atmasına neden olacağından, uyumsuz türleri kullanırken daralmayı işe yaramaz hale getirmez.
ThePyroEagle

6
Yuvarlak değil. Yayınlandı (= kesildi)
Lukas Eder

483

Bu dökümün iyi bir örneği * = veya / = kullanmaktır

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

veya

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

veya

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

veya

char ch = 'A';
ch *= 1.5;
System.out.println(ch); // prints 'a'

11
@AkshatAgarwal ch bir karakterdir. 65 * 1.5 = 97.5 -> Anladın mı?
Sajal Dutta

79
Evet, ama buraya yeni başlayanların geldiğini, okuduğunu ve uzaklaşıp herhangi bir karakteri büyük harften 1.5'e çarparak küçük harfe dönüştürebileceğinizi düşünerek görebiliyorum.
Dawood ibn Kareem

103
@DavidWallace Sürece herhangi bir karakter A;)
Peter Lawrey

14
@PeterLawrey & @DavidWallace senin ortaya çıkaracaktır sırrı - ch += 32 = D
Minhas Kamal

256

Çok güzel bir soru. Java Dil şartname önerinizi doğruluyor.

Örneğin, aşağıdaki kod doğrudur:

short x = 3;
x += 4.6;

ve x'in 7 değerine sahip olmasıyla sonuçlanır, çünkü şuna eşdeğerdir:

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

15
Veya daha eğlenceli: "int x = 33333333; x + = 1.0f;".
supercat

5
@supercat, bu ne hileli? Yanlış bir şekilde yuvarlanan genişleyen bir dönüşüm ve ardından sonucu gerçekten değiştirmeyen bir ekleme, normal insan zihinleri için en beklenmedik bir sonuç üretmek üzere tekrar int'e döküm.
neXus

1
@neXus: IMHO, dönüşüm kurallarına double->float, tür değerlerinin floatgerçek sayıları türden daha az spesifik olarak tanımlaması temelinde genişletme olarak ele alınmış olmalıdır double. Biri doubletam bir posta adresi ve float5 basamaklı bir posta kodu olarak görünüyorsa, tam bir adres verilen bir posta kodu talebini karşılamak mümkündür, ancak yalnızca bir posta kodu verilen tam bir adres için doğru bir istek belirtmek mümkün değildir. . Bir sokak adresini posta koduna dönüştürmek kayıplı bir işlemdir, ancak ...
Supercat

1
... tam bir adrese ihtiyaç duyan biri genellikle sadece bir posta kodu istemez. İle dönüşüm, float->doubleABD posta kodu 90210'u "ABD Postanesi, Beverly Hills CA 90210" ile dönüştürmeye eşdeğerdir.
supercat

181

Evet,

temelde yazdığımızda

i += l; 

derleyici bunu

i = (int)(i + l);

Az önce .classdosya kodunu kontrol ettim .

Gerçekten bilmek iyi bir şey


3
Bunun hangi sınıf dosyası olduğunu söyleyebilir misin?
nanofarad

6
@hexafraction: sınıf dosyası ile ne demek istiyorsun? Eğer benim yazımda bahsettiğim sınıf dosyası hakkında soruyorsanız, java sınıfınızın uygun sürümü
Umesh Awasthi

3
Oh, "sınıf" kodundan bahsettin, bu da beni belirli bir sınıf dosyasının içerdiğine inandırdı. Şimdi ne demek istediğini anlıyorum.
nanofarad

@Bogdan Düzgün kullanılan yazı tipleriyle ilgili bir sorun olmamalıdır. Programlama için yanlış yazı tipini seçen bir programcı, nasıl devam edileceğini açıkça düşünmelidir ...
glglgl

7
@glglgl Bu durumlarda ayırt etmek için yazı tipine güvenilmesi gerektiğine katılmıyorum ... ama herkes en iyi neyin düşündüğünü seçme özgürlüğüne sahiptir.
Bogdan Alexandru

92

Eğer gelen döküm gerekir longetmek int explicitlydurumunda i = i + l o zaman derlemek ve doğru çıktı verecektir. sevmek

i = i + (int)l;

veya

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

ancak bu durumda +=iyi çalışır çünkü operatör dolaylı olarak sağ değişken türünden sol değişken türüne döküm türünü dolaylı olarak dökmek zorunda değildir.


7
Bu durumda, "örtük kast" kayıplı olabilir. Gerçekte, onun cevabını @LukasEder devletler olarak, hiç dökme intyapılır sonra+ . Gerçekten döküm eğer derleyici (? Olmalı) bir uyarı atmak istiyorum longiçin int.
Romain

63

Buradaki sorun tip dökümü içerir.

İnt ve long eklediğinizde,

  1. İnt nesnesi uzun süre dökülür ve her ikisi de eklenir ve uzun nesne elde edersiniz.
  2. ancak uzun nesneler dolaylı olarak int. Yani, bunu açıkça yapmalısınız.

Ancak +=döküm yapacağı şekilde kodlanmıştır.i=(int)(i+m)


54

Java türünde, bir atama işleminin sağ tarafındaki ifadenin türü, atamanın sol tarafındaki değişkenin türüne güvenli bir şekilde yükseltilebildiğinde, dönüşümler otomatik olarak gerçekleştirilir. Böylece güvenle atayabiliriz:

 bayt -> kısa -> int -> uzun -> şamandıra -> çift. 

Aynı şey ters yönde çalışmaz. Örneğin, birinciyi otomatik olarak uzun bir int'e dönüştüremeyiz çünkü ilki ikincisinden daha fazla depolama alanı gerektirir ve sonuç olarak bilgiler kaybolabilir. Böyle bir dönüşümü zorlamak için açık bir dönüşüm gerçekleştirmeliyiz.
Tür - Dönüşüm


2
Hey, ama long2 kat daha büyük float.
Görünen Ad

11
A floatmümkün olan her intdeğeri tutamaz ve doublemümkün olan her longdeğeri tutamaz .
Alex MDC

2
"Güvenle dönüştürülmüş" ile ne demek istiyorsun? Cevabın ikinci kısmından, şamandıra durumunda otomatik olmayan (örtük döküm) demek istediğinizi söyleyebilirim -> float durumunda elbette doğru değil. şamandıra pi = 3.14f; uzun b = pi; derleyici hatasına neden olur.
Luke

1
Kayan nokta ilkel tiplerini tamsayı ilkel tiplerle ayırt etmek daha iyi olur. Aynı şey değiller.
ThePyroEagle

Java, aldatmasız davranışların beklentileri karşılayacağı birçok kalıpta alçı kullanılmasını gerektiren basit dönüşüm kurallarına sahiptir, ancak genellikle hatalı olan birçok kalıpta alçı gerektirmez. Örneğin, double d=33333333+1.0f;33333332.0 sonucu büyük olasılıkla amaçlanmamış olsa da (derhal 33333334.0f'nin aritmetik olarak doğru cevabı ya floatda olarak temsil edilebilir int) , bir derleyici şikayetsiz olarak kabul edecektir .
supercat

46

Bazen böyle bir soru röportajda sorulabilir.

Örneğin, yazarken:

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

otomatik tipleme yoktur. C ++ 'da yukarıdaki kodu derlerken herhangi bir hata olmayacak, ancak Java'da böyle bir şey alacaksınız Incompatible type exception.

Bundan kaçınmak için kodunuzu şu şekilde yazmalısınız:

int a = 2;
long b = 3;
a += b;// No compilation error or any exception due to the auto typecasting

6
opC ++ 'da kullanımın Java'daki kullanımıyla karşılaştırılmasına ilişkin anlayış için teşekkürler . Her zaman bu trivia parçalarını görmeyi severim ve sık sık dışarıda bırakılabilecek sohbete bir katkıda bulunduğunu düşünüyorum.
Thomas

2
Sorunun kendisi Ancak olan ilginç, bu soran bir röportajda aptalca. Kişinin iyi bir kalite kodu üretebileceğini kanıtlamaz - sadece bir Oracle sertifika sınavına hazırlanmak için yeterli sabrı olduğunu kanıtlar. Ve tehlikeli bir otomatik dönüşüm kullanarak uyumsuz türlerden "kaçınmak" ve böylece olası taşma hatasını gizlemek, muhtemelen kişinin olası kaliteli bir kod üretemediğini bile kanıtlar . Tüm bu otomatik dönüşümler ve otomatik bokslar ve hepsi için Java yazarlarına lanet olsun!
Honza Zidek

25

Temel fark, devam eden bir a = a + btiplemenin olmamasıdır ve bu nedenle derleyici, tiplemelemediğiniz için size kızar. Ancak bununla a += bgerçekten yaptığı şey, buyumlu bir tipe yazmaktır a. Yani eğer yaparsan

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

Gerçekten yaptığınız şey:

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

5
Bileşik atama işleçleri, sağdaki işlenene değil, ikili işlem sonucunun daralma dönüşümünü gerçekleştirir. Yani örneğinizde, 'a + = b', 'a = a + (int) b' ile eşdeğer değildir, ancak burada diğer cevaplarla açıklandığı gibi, 'a = (int) (a + b)' ile eşdeğerdir.
Lew Bloch

13

Burada ince bir nokta ...

i+jWhen jbir double ve iint olduğunda örtük bir typecast vardır . Java DAİMA aralarında bir işlem olduğunda bir tamsayıyı ikiye dönüştürür.

i+=jNerede ibir tam sayı ve jbir çift olduğunu açıklığa kavuşturmak

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

Bkz: örtük dökümün bu açıklaması

Sen typecasting isteyebilirsiniz jiçin (int)netlik için bu durumda.


1
Bence daha ilginç bir durum olabilir int someInt = 16777217; float someFloat = 0.0f; someInt += someFloat;. İçin sıfır ekleme someIntdeğerini etkilememesi gerektiğini, ancak teşvik someIntetmek floatdeğerini değiştirebilir.
supercat

5

Java Dil Şartname tanımlar E1 op= E2eşdeğer olduğu E1 = (T) ((E1) op (E2))yerde Ttürüdür E1ve E1bir kez değerlendirilir .

Bu teknik bir cevap, ama bunun neden böyle olduğunu merak ediyor olabilirsiniz. Peki, aşağıdaki programı ele alalım.

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

Bu program ne yazdırıyor?

3'ü tahmin ettin mi? Çok kötü, bu program derlenmeyecek. Neden? Java'da bayt eklemenin birint . Bunun nedeni, Java Sanal Makinesi'nin bayt kodlarını kaydetmek için bayt işlemlerini tanımlamamış olmasından kaynaklanmaktadır (sonuçta sınırlı sayıda vardır), bunun yerine tamsayı işlemlerini bir dilde açıklanmış bir uygulama detayı kullanmaktır.

Fakat eğer a = a + bişe yaramazsa, a += bbu E1 += E2, tanımlandığı takdirde baytlar için asla işe yaramayacağı anlamına gelir E1 = E1 + E2. Önceki örnekte gösterildiği gibi, bu gerçekten de durum böyle olurdu. Yapmak için bir hack olarak +=bayt ve şort operatör çalışmaları, ilgili örtük dökme yoktur. Bu bir hack büyük değil, ama Java 1.0 çalışması sırasında, odak noktası dili başlamak için başladı. Artık geriye dönük uyumluluk nedeniyle Java 1.0'da tanıtılan bu saldırı kaldırılamadı.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.