본문 바로가기

백엔드/Java

[Java] Implicit Narrowing Conversion에 관하여

 

 

 

 

 

 

자바에서는 컴파일러가 기본적으로 정수 리터럴을 int 타입으로 간주한다. 그런데, 이와 관련해서 갑자기 자바의 근원적인 부분에서 궁금증이 떠올랐다. 예를 들어
 
 

byte a = 1;

 
 
위의 코드에서 컴파일러가 1을 int 타입으로 간주하면, byte 타입이 int 타입보다 허용 범위가 작기 때문에 자동 타입 변환(Promotion)이 되지 않을 텐데도 불구하고, 어떻게 int 타입인 1이 컴파일 에러를 일으키지 않고 byte 타입 변수에 대입되는지에 대한 의문이 들었다. 
 
 
일반적으로 흔히 배우는 '자동 타입 변환(Promotion)'과 '명시적 타입 변환(Casting)'으로는 위의 의문이 설명되지 않았다. 분명히 숨겨진 원리가 있을 것이라고 확신하며, 다양한 곳에서 이에 대해 질문하고 찾아보았다. 하지만 마땅한 답을 듣지 못했고, 점점 궁금증이 증폭되었다. 이후 오라클(Oracle)이 주관하는 자바 공식 문서인 '자바 언어 규약(Java Language Specification)'까지 처음으로 뒤져보게 되었다. 그러다가 마침내 해답을 얻었다...
 
 
https://docs.oracle.com/javase/specs/

 

Java SE Specifications

Java Language and Virtual Machine Specifications Java SE 19 Released September 2022 as JSR 394 The Java Language Specification, Java SE 19 Edition HTML | PDF Preview feature: Pattern Matching for switch Preview feature: Record Patterns The Java Virtual Mac

docs.oracle.com

 
 
그 해답은 바로 자바 언어 규약 5.2 'Assignment Contexts'에 자세히 나와있다. 자바 언어 규약 문서에서는 Implicit Narrowing Conversion 단어에 대한 구체적인 명시는 없지만, 이에 대한 내용은 깊이 있게 적혀있다.  '암묵적 축소 변환(Implicit Narrowing Conversion)' 은 '자동 타입 변환(Promotion)'과 '명시적 타입 변환(Casting)'과는 다른 개념이다. 암묵적 축소 변환에 대하여 간단히 설명하자면, 큰 허용 범위의 타입에서 작은 허용 범위의 타입으로 명시적 타입 변환 없이 자동으로 변환해 준다는 규약이다. 쉽게 생각하면 자동 타입 변환의 정반대라고 볼 수 있다.
 
 
하지만, 암묵적 축소 변환은 특정한 조건에서만 허용된다. 큰 범위의 데이터 타입의 값이 작은 범위의 데이터 타입의 변수에 할당될 때, 그 값이 데이터의 손실 없이 더 작은 범위의 데이터 타입으로 안전하게 표현될 수 있을 때만, 암묵적 축소 변환이 자바 컴파일러에 의해 자동으로 수행된다. 예를 들어, byte 타입인 경우에서 byte 타입의 범위가 -128~127이므로 이 범위 내에 있는 정수 리터럴은 암묵적 축소 변환의 조건에 충족하므로 자동으로 값이 byte 타입으로 변환이 이루어진다.
 
 
자바 언어 규약 문서 129페이지에는 내가 궁금했던 내용에 대한 부분이 직접적으로 나와있다. 그 내용은 아래와 같다.
 
 

Java Language Specification, 129p

 
 
• A narrowing primitive conversion may be used if the variable is of type byte, short, or char, and the value of the constant expression is representable in the type of the variable.
 
• A narrowing primitive conversion followed by a boxing conversion may be used if the variable is of type Byte, Short, or Character, and the value of the constant expression is representable in the type byte, short, or char respectively.
 

     The compile-time narrowing of constant expressions means that code such as:
 
     byte theAnswer = 42;
 
     is allowed. Without the narrowing, the fact that the integer literal 42 has type int would mean that a cast         to byte would be required:
 
     byte theAnswer = (byte)42;  //  cast is permitted but not required
 
 
발췌한 내용에서 예로 든 코드에 대해 부연설명을 하자면, 리터럴 42를 byte 타입에 대입하고 있는데 42는 int 타입으로 취급되므로 원래라면 타입 불일치에 의한 컴파일 에러가 발생해야 되지만, 실제로는 42가 byte 타입의 범위 내에 있으므로 암묵적 축소 변환이 수행되어 자동으로 byte 인 42의 타입이 int로 변경되어 변수 theAnswer에 할당된다. 발췌한 내용의 맨 밑에 줄 주석에도 명시적 형변환이 필요되지 않는다고 나와있다.
 
 
이처럼 내가 처음 제시한 byte a = 1; 의 코드도 위와 마찬가지의 방식으로 Implicit Narrowing Conversion이 수행되기 때문에 컴파일 에러가 발생하지 않는 것을 알 수 있다.

 
 
 

'백엔드 > Java' 카테고리의 다른 글

[Java] 스트림 Reduce 메서드의 내부 로직에 관하여  (0) 2023.09.20