Javaの+ =、-=、* =、/ =複合代入演算子がキャストを必要としないのはなぜですか?


3633

今日まで、私は例えばそれを考えました:

i += j;

のショートカットでした:

i = i + j;

しかし、これを試してみると:

int i = 5;
long j = 8;

その後i = i + j;はコンパイルされませんi += j;が、正常にコンパイルされます。

それは実際にi += j;はこのようなものへのショートカット であることを意味しi = (type of i) (i + j)ますか?


135
Javaがこれを許可し、以前のバージョンよりも厳密な言語であることに驚いています。キャストのエラーは重大な失敗につながる可能性があります。Ariane5Flight 501の場合と同様に、64ビットの浮動小数点を16ビットの整数にキャストするとクラッシュが発生しました。
SQLDiver

103
Javaで記述されたフライトコントロールシステムでは、これはあなたの心配の中で最も少ない@SQLDiver
Ross Drew

10
実際にi+=(long)j;うまくコンパイルされます。
Tharindu Sathischandra 2016年

6
正確さを求める開発者と使いやすさを求める開発者の絶え間ない努力は本当に興味深いものです。言語の2つのバージョンがほぼ必要です。1つは驚くほど正確で、もう1つは使いやすいバージョンです。Javaを両方向から押すと、Javaはどちらのグループにも適さなくなります。
ビルK

5
キャストが必要な場合、どこに配置しますか?i += (int) f;加算の前にfをキャストするため、同等ではありません。(int) i += f;代入後に結果をキャストします。同等ではありません。追加後、代入前に値をキャストすることを示すキャストを配置する場所はありません。
Norill Tempest 2018

回答:


2441

これらの質問でいつもそうであるように、JLSは答えを保持します。この場合、§15.26.2複合代入演算子。抽出物:

フォームの化合物代入式は、E1 op= E2と等価であるE1 = (T)((E1) op (E2))場合、TのタイプがあるE1ことを除いて、E1一度だけ評価されます。

§15.26.2から引用した例

[...]次のコードは正しいです:

short x = 3;
x += 4.6;

xの値は7になります。これは次と同等であるためです。

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

言い換えれば、あなたの仮定は正しいです。


42
だからi+=j私が自分でチェックしたようにコンパイルしますが、正確さが失われるでしょう?その場合、なぜi = i + jでも発生しないのですか?なぜそこに私たちを悩ませますか?
bad_keypoints 2012

46
@ronnieaka:言語デザイナーは、あるケース(i += j)では、他のケース(i = i + j)とは対照的に精度の低下が望まれると想定した方が安全だと感じたと思います
Lukas Eder

12
いいえ、そのすぐ前にあります。早く気づかなかったのでごめんなさい。あなたの答えのようにE1 op= E2 is equivalent to E1 = (T)((E1) op (E2))、それは暗黙的なダウン型キャストのようなものです(longからintへのダウン)。私は+ jは=でのに対し、我々は明示的にそれをしなければならない、すなわち、提供(T)中の部分をE1 = ((E1) op (E2))それがあるではありませんか?
bad_keypoints 2012

11
Javaコンパイラが型キャストを追加する理由として考えられるのは、互換性のない型に対して算術演算を実行しようとすると、短縮形を使用して結果の型キャストを実行する方法がないためです。結果の型キャストは一般に、問題のある引数の型キャストよりも正確です。型キャストでは、互換性のない型を使用すると、コンパイラーが常にエラーをスローするため、縮小が役に立たなくなります。
ThePyroEagle

6
丸められていません。キャスト(=切り捨て)
Lukas Eder、

483

このキャストの良い例は、* =または/ =の使用です。

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

または

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

または

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

または

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

11
@AkshatAgarwal chはcharです。65 * 1.5 = 97.5->わかりましたか?
Sajal Dutta、2014

79
ええ、でも、初心者がここに来て、これを読んで、1.5を掛けることで、大文字から小文字に変換できると思ってしまうのを見ることができます。
Dawood ibnカリーム2014年

103
@DavidWallaceである限り、任意の文字A;)
Peter Lawrey 2014年

14
@PeterLawrey&@DavidWallace私はあなたの秘密を明らかにします- ch += 32 = D
Minhas Kamal

256

非常に良い質問です。Java言語仕様では、あなたの提案を確認しました。

たとえば、次のコードは正しいです。

short x = 3;
x += 4.6;

xの値は7になります。これは次と同等であるためです。

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

15
またはもっと楽しい:「int x = 33333333; x + = 1.0f;」。
スーパーキャット2017

5
@supercat、これはどんなトリックですか?誤って丸められた拡大変換の後に、実際には結果を変更しない追加が続き、再びintにキャストして、通常の人間の心にとって最も予期しない結果を生成します。
ネクサス2018

1
@neXus:IMHO double->float、typeの値はtype floatよりも具体的に実数を識別しないことに基づいて、変換ルールは拡張として扱われるべきでしたdoubledouble完全な住所およびfloat5桁の郵便番号として表示する場合、完全な住所が指定された郵便番号の要求を満たすことはできますが、郵便番号だけが指定された完全な住所の要求を正確に指定することはできません。 。住所を郵便番号に変換することは損失を
伴う

1
...完全な住所が必要な人は、通常、郵便番号だけを要求することはありません。からの変換float->doubleは、「US Post Office、Beverly Hills CA 90210」で米国の郵便番号90210を変換することと同じです。
スーパーキャット2018

181

はい、

基本的に私たちが書くとき

i += l; 

コンパイラはこれを

i = (int)(i + l);

私はちょうどチェックした .classファイルコードを。

本当に知っておきたいこと


3
これがどのクラスファイルか教えていただけますか?
ナノファラッド

6
@hexafraction:クラスファイルとはどういう意味ですか?あなたはクラスファイルについて尋ねた場合、私はそれはあなたのJavaクラスの遵守バージョンのものより私のポストに言及
Umesh Awasthi

3
ああ、あなたは "the"クラスファイルのコードについて言及しましたが、特定のクラスファイルが関係していると私に思わせました。私はあなたの今の意味を理解しています。
nanofarad 2013

@Bogdanそれは適切に使用されたフォントの問題ではないはずです。プログラミングに間違ったフォントを選択するプログラマは、続行方法を明確に
検討

7
@glglglそれらの場合に区別するためにフォントに依存するべきだとは思わない...しかし、誰もが最良と考えるものを選択する自由を持っている。
Bogdan Alexandru

92

あなたからのキャストに必要longint explicitlyする場合にはi = i + l 、それをコンパイルし、正しい出力が得られます。お気に入り

i = i + (int)l;

または

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

ただし+=、演算子は暗黙的に右変数の型から左変数の型への型キャストを行うため、明示的にキャストする必要がないため、正常に機能します。


7
この場合、「暗黙のキャスト」は不可逆になる可能性があります。実際には、@ LukasEderが回答で述べているように、キャスト先intはのに実行さます+。コンパイラがlongtoを実際にキャストした場合、警告をスローします(すべきですか?)int
ロメイン

63

ここでの問題は型キャストに関係しています。

intとlongを追加すると、

  1. intオブジェクトはlongにキャストされ、両方が追加され、長いオブジェクトが取得されます。
  2. ただし、長いオブジェクトを暗黙的にintにキャストすることはできません。したがって、明示的に行う必要があります。

しかし+=、型キャストを行うようにコーディングされています。i=(int)(i+m)


54

Javaでは、代入演算の右側の式の型を代入の左側の変数の型に安全に昇格できる場合、型変換が自動的に実行されます。したがって、安全に割り当てることができます。

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

同じことが逆に機能しません。たとえば、1つ目は2つ目よりも多くのストレージを必要とし、その結果情報が失われる可能性があるため、longをintに自動的に変換することはできません。そのような変換を強制するには、明示的な変換を実行する必要があります。
タイプ-変換


2
ねえ、しかしlongよりも2倍大きいですfloat
表示名

11
floatあらゆる可能な保持できないint価値を、そしてdoubleあらゆる可能性を保持することはできませんlong値を。
Alex MDC 2013

2
「安全に変換」とはどういう意味ですか?回答の後半から、自動変換(暗黙のキャスト)を意味していると推測できますが、これはもちろんfloat-> longの場合には当てはまりません。float pi = 3.14f; long b = pi; コンパイラエラーが発生します。
ルーク

1
浮動小数点プリミティブ型を整数プリミティブ型と区別することをお勧めします。彼らは同じものではありません。
ThePyroEagle

Javaには単純な変換ルールがあり、多くのパターンでキャストを使用する必要があるため、キャストを使用しない場合の動作は期待と一致しますが、通常はエラーのある多くのパターンでのキャストは必要ありません。たとえばdouble d=33333333+1.0f;、結果33333332.0が意図したものではない可能性があるとしても、コンパイラは文句なしに受け入れます(偶然にも、33333334.0fの算術的に正しい答えは、floatまたはのいずれかとして表現できますint)。
スーパーキャット2017

46

時々、インタビューでそのような質問をすることができます。

たとえば、次のように書いた場合:

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

自動型キャストはありません。C ++では上記のコードをコンパイルしてもエラーは発生しませんが、Javaでは次のような結果になります。Incompatible type exceptionます。

したがって、それを回避するには、次のようにコードを記述する必要があります。

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

6
opC ++での使用とJava での使用の比較に関する洞察に感謝します。私はいつもこれらの雑学クイズを見るのが好きで、会話のなかで省略されることがあると思います。
Thomas

2
しかし、質問自体興味深いものであり、インタビューでこれ尋ねることは愚かです。これは、その人物が良質のコードを作成できることを証明するものではありません。Oracle認定試験の準備に十分な忍耐力があることを証明するだけです。危険な自動変換を使用して互換性のないタイプを「回避」し、起こり得るオーバーフローエラーを非表示にすると、おそらくその人が高品質のコードを生成できないことが証明されます。これらすべての自動変換と自動ボックス化などすべてについてJavaの作者を酷評してください!
Honza Zidek 2018年

25

主な違いは、a = a + bでは型キャストが行われていないため、型キャストを行わないとコンパイラーが怒るということです。しかし、a += bでは、実際に行っていることはb、と互換性のある型への型キャストaです。だからあなたがするなら

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

あなたが本当にやっていることは:

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

5
複合代入演算子は、右側のオペランドではなく、二項演算の結果のナローイング変換を実行します。したがって、あなたの例では、「a + = b」は「a = a +(int)b」と同等ではありませんが、ここで他の回答で説明されているように、「a =(int)(a + b)」に等しくなります。
Lew Bloch

13

ここの微妙なポイント...

i+jwhen jがdoubleで、iがintの場合、暗黙の型キャストがあります。Javaは常に、それらの間に演算がある場合、整数をdoubleに変換します。

が整数とdoubleのi+=j場所を明確にするにiは、次のjように記述できます。

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

参照:この暗黙のキャストの説明

この場合は、わかりやすくするために型キャストjすることをお勧めし(int)ます。


1
もっと面白いケースがあると思いますint someInt = 16777217; float someFloat = 0.0f; someInt += someFloat;。に0を追加someIntsomeIntfloatもその値には影響しませんが、値を変更すると値が変わる可能性があります。
スーパーキャット2018

5

Java言語仕様は、がのタイプである同等であると定義E1 op= E2されE1 = (T) ((E1) op (E2))Tおり、一度評価されE1E1ます。

これは技術的な答えですが、なぜそうなるのか疑問に思われるかもしれません。さて、次のプログラムを考えてみましょう。

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

このプログラムは何を印刷しますか?

3つ当てた?残念ながら、このプログラムはコンパイルできません。どうして?まあ、たまたま、Javaでのバイトの追加がを返すように定義されていintます。これは、Java仮想マシンがバイトコードを保存するバイト操作を定義していないためです(結局、数に制限があります)。代わりに整数操作を使用すると、言語で公開される実装の詳細になります。

しかし、a = a + b動作しないa += b場合、それE1 += E2がと定義されている場合、バイトに対して動作しないことを意味しますE1 = E1 + E2。前の例が示すように、それは実際のケースです。+=演算子をバイトやショートで機能させるハックとして、暗黙のキャストが含まれます。それはそれほど素晴らしいハックではありませんが、Java 1.0の作業中は、最初から言語をリリースすることに焦点を当てていました。現在、下位互換性のため、Java 1.0で導入されたこのハックは削除できませんでした。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.