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가 이전 언어보다 엄격한 언어로 이것을 허용한다는 사실에 놀랐습니다. 16 비트 정수로 64 비트 부동 캐스트가 충돌을 일으킨 Ariane5 Flight 501의 경우와 같이 캐스팅 오류로 인해 심각한 오류가 발생할 수 있습니다.
SQLDiver

103
Java로 작성된 비행 제어 시스템에서는 이것이 걱정할 것입니다. @SQLDiver
Ross Drew

10
실제로 i+=(long)j;는 잘 컴파일됩니다.
Tharindu Sathischandra

6
정확성과 사용 편의성을 위해 한 개발자 세트가 지속적으로 추진하는 것은 정말 흥미 롭습니다. 우리는 거의 두 가지 버전의 언어가 필요합니다. 하나는 놀랍도록 정확하고 다른 하나는 사용하기 쉽습니다. Java를 양방향에서 밀면 어느 그룹에도 적합하지 않은쪽으로 이동합니다.
Bill K

5
캐스팅이 필요한 경우 어디에 배치 하시겠습니까? i += (int) f;더하기 전에 f를 캐스팅하므로 같지 않습니다. (int) i += f;할당 후에도 결과를 캐스팅합니다. 추가 후, 할당 전에 값을 캐스트하려는 것을 표시하는 캐스트를 넣을 장소가 없습니다.
Norill Tempest

답변:


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

46
@ronnieaka : 나는 언어 설계자가 하나의 경우 (에 있다고 생각 같은데요 i += j()가 다른 경우와 달리 정밀도의 손실이 요구되어 있다고 가정하는 것이 안전하다 i = i + j)
루카스 에더

12
아뇨, 바로 저 앞에서 요! 미안하지만 일찍 알아 차리지 못했습니다. 귀하의 답변 에서처럼 E1 op= E2 is equivalent to E1 = (T)((E1) op (E2)), 이것은 암시 적 다운 타입 캐스팅 (long to int)과 같습니다. 나는에게 + J를 =에있는 반면, 우리는 명시 적으로 그것을 즉, 제공 할 필요 (T)에 참여 E1 = ((E1) op (E2))하지 그것을?
bad_keypoints

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는 문자입니다. 65 * 1.5 = 97.5-> 알았습니까?
Sajal Dutta

79
예, 그러나 초보자가 여기에 와서 이것을 읽고, 1.5를 곱하면 모든 문자를 대문자에서 소문자로 변환 할 수 있다고 생각합니다.
Dawood ibn Kareem

103
@DavidWallace A;) 모든 문자 ;)
Peter Lawrey

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;".
supercat

5
@ supercat, 이것은 어떤 속임수입니까? 변환이 잘못 반올림되고 실제로 결과가 변경되지 않는 추가가 이어지고, int로 다시 캐스팅하여 정상적인 인간의 마음에 가장 예상치 못한 결과를 생성합니다.
neXus

1
@neXus : IMHO, 변환 규칙은 double->floattype 값이 type float보다 실수를 덜 구체적으로 식별 한다는 점을 기초로 확대로 처리해야 합니다 double. 만약 하나의 전망 double완전한 우편 주소와 float, 그것은 완전한 주소 주어진 우편 번호에 대한 요구를 만족하는 것이 가능하지만, 정확하게 단지 우편 번호를 부여 완전한 주소에 대한 요청을 지정할 수는 없습니다 5 자리 우편 번호 등 . 주소를 우편 번호로 변환하는 작업은 손실이 있지만 ...
supercat

1
전체 주소가 필요한 사람은 일반적으로 우편 번호 만 요구하지 않습니다. 변환은 float->double"미국 우체국, 비벌리 힐스 CA 90210"으로 미국 우편 번호 90210을 변환하는 것과 같습니다.
supercat

181

예,

기본적으로 우리가 쓸 때

i += l; 

컴파일러는 이것을 이것을

i = (int)(i + l);

방금 .class파일 코드를 확인했습니다 .

정말 좋은 점


3
이것이 어떤 클래스 파일인지 말해 줄 수 있습니까?
nanofarad

6
@hexafraction : 클래스 파일의 의미는 무엇입니까? 당신은 클래스 파일에 대해 묻는 경우 나는 그것이 당신의 자바 클래스의 컴파일 된 버전의보다 내 게시물에서 언급 한
Umesh Awasthi에게

3
오, 당신은 "the"클래스 파일 코드를 언급했는데, 그로 인해 특정 클래스 파일이 관련되었다고 생각하게되었습니다. 나는 당신이 지금 무슨 뜻인지 이해합니다.
나노 패럿

@Bogdan 제대로 사용 된 글꼴에는 문제가되지 않습니다. 프로그래밍을 위해 잘못된 글꼴을 선택하는 프로그래머는 진행 방법을 분명히 생각해야합니다.
glglgl

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.
Romain

63

여기서 문제는 유형 캐스팅과 관련이 있습니다.

int와 long을 추가하면

  1. int 객체는 long으로 캐스트되고 둘 다 추가되며 긴 객체를 얻습니다.
  2. 그러나 긴 객체는 암시 적으로 int로 캐스팅 할 수 없습니다. 따라서 명시 적으로해야합니다.

그러나 +=유형 캐스팅을 수행하는 방식으로 코딩됩니다.i=(int)(i+m)


54

할당 유형의 오른쪽에있는 표현식 유형이 할당의 왼쪽에있는 변수 유형으로 안전하게 승격 될 수있는 경우 Java 유형 변환은 자동으로 수행됩니다. 따라서 다음을 안전하게 할당 할 수 있습니다.

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

다른 방식으로는 동일하게 작동하지 않습니다. 예를 들어 첫 번째는 두 번째보다 많은 저장 공간이 필요하므로 정보가 손실 될 수 있기 때문에 long을 int로 자동 변환 할 수 없습니다. 그러한 개종을 강요하려면 명시적인 개종을 수행해야합니다.
유형-변환


2
이봐,하지만 long보다 2 배 더 큽니다 float.
표시 이름

11
A float는 가능한 모든 int값을 double보유 할 수 없으며 가능한 모든 long값을 보유 할 수 없습니다 .
Alex MDC

2
"안전하게 변환 된"은 무슨 뜻입니까? 대답의 후반부에서 자동 변환 (암시 적 캐스트)을 의미한다고 생각할 수 있습니다 .float-> long의 경우에는 물론 그렇지 않습니다. 플로트 파이 = 3.14f; 긴 b = pi; 컴파일러 오류가 발생합니다.
Luke

1
부동 소수점 기본 유형을 정수 기본 유형으로 차별화하는 것이 좋습니다. 그들은 같은 것이 아닙니다.
ThePyroEagle

Java에는 캐스트가없는 동작이 예상과 달리 많은 패턴에서 캐스트를 사용해야하는 단순한 변환 규칙이 있지만 일반적으로 잘못된 패턴이 많은 캐스트는 필요하지 않습니다. 예를 들어, 컴파일러가 허용하는 double d=33333333+1.0f;결과 33333332.0 가능성이 의도 된 것이 아닐 것에도 불구하고, 불만없이 (덧붙여 말하자면, 33333334.0f의 산술적 - 정답은 하나로서 표현할 것 float또는 int).
supercat

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 작성자를 망쳐 놓으십시오!
혼자 지덱

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 2016 년

13

미묘한 점은 ...

double 이 int 인 i+j경우 암시 적 타입 캐스트가 있습니다 . 자바 항상ji 정수 사이에 연산이있을 때 정수를 double로 변환합니다.

정수가 i+=j어디에 있고 더블 인지 명확히하기 위해 다음과 같이 설명 할 수 있습니다ij

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

참조 : 암시 적 캐스팅에 대한이 설명

이 경우 명확성 j을 위해 typecast 를 입력 할 수 있습니다 (int).


1
더 흥미로운 경우가있을 것 같습니다 int someInt = 16777217; float someFloat = 0.0f; someInt += someFloat;. 제로 추가 someInt값에 영향을주지해야하지만 홍보 someInt하기 위해 float그 값을 변경할 수 있습니다.
supercat

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 Virtual Machine은 바이트 코드를 저장하기 위해 바이트 작업을 정의하지 않기 때문에 (결국 제한된 수의 숫자가 있음) 정수 연산을 사용하는 대신 언어에 노출 된 구현 세부 사항입니다.

그러나 작동 a = a + b하지 않으면 로 정의 된 a += b경우 바이트에서 작동하지 않을 것입니다 . 앞의 예에서 알 수 있듯이 실제로는 그럴 것입니다. 바이트와 ​​쇼트에 대해 연산자를 작동 시키는 해킹으로 암시 적 캐스트가 수반됩니다. 그다지 해킹은 아니지만 Java 1.0 작업 중에는 언어를 처음 출시하는 데 중점을 두었습니다. 이전 버전과의 호환성으로 인해 Java 1.0에 도입 된이 핵을 제거 할 수 없었습니다.E1 += E2E1 = E1 + E2+=

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.