Java (자바)

[Java] 형 변환 Type Casting / String <-> int / 정수 <-> 실수 / 정수 <-> 정수 / 오버플로우

Oscar:) 2022. 10. 5. 19:44

 

공부를 계속하며 이런저런 코드를 작성하다보면,

서로 다른 타입의 변수를 대입하거나 연산해야 하는 경우를 맞이한다.

 

자바는 타입에 예민하기 때문에,

타입이 불일치한 상태에서 대입&연산을 시도하면 대부분 에러를 내뿜는다.

 

그렇기에 변수를 대입&연산 하기 전, 타입을 일치시켜주는 과정을 거쳐야 하는데

이를 형 변환(Casting) 이라 한다.

 

 

이번 포스팅에서는, 형 변환에 대해 알아보겠다.

 

 


 

형 변환 기초 개념

 

● 기본형 자료형 중, 논리 타입(boolean)을 제외한 나머지 자료형은

모두 형 변환이 가능하다.

 

 

● 형 변환이라는 말 그대로,

해당 변수의 타입을 변환해 줄 뿐, 값은 유지되어야 한다는 근본이 있다.

ex)
정수 타입 int : 100  =>  문자열 String : "100"
 =>  정수 타입 int : 100

 

- 하지만, 일부의 경우에 한해서 값이 변동될 수도 있다.

ex)
실수 타입 double : 10.5  =>  정수 타입 int : 10

 

실수 타입이 정수 타입으로 변환될 때는,

소수점이 과감하게 버려지는 케이스다.

 

변환을 적용하게 해주는 대신,

정수 타입답게, 정수까지만 인정해주는 모습이다.

 

 

● 변환 후에도 타입이 불일치 한다면, 형 변환을 적용할 수 없다.

 

ex)
문자열 String "오스카"  =>  정수 타입 int : 에러(변환 불가)

 

 


 

형 변환 예제

 

이제 형 변환을 적용할 때 작성하는 코드를 알아보자.

 

 

● 문자열 String <=> 정수 타입 int

 

형 변환을 가장 많이 사용하는 케이스다.

 

 

· String => int 변환

	String s = "10";
		
	int i1 = Integer.parseInt(s);
	int i2 = Integer.valueOf(s);

 

위처럼 Integer 클래스의 parseInt(), valueOf() 메서드를 사용한다.

 

두 가지 메서드 모두 int 타입으로 변환해주고, 결과 값도 같다.

차이점이 있다면 다음과 같다.

 

- parseInt() : 기본형 자료형 int 타입을 반환해준다.

- valueOf() : int 타입의 Wrapper Class인 Integer 객체를 반환해준다.

 

 

 

실제로 Integer 클래스를 찾아가 확인해보니,

parseInt() 메서드는 valueOf() 메서드 안에 포함되어 있었으며,

 

형태만을 Integer 객체로서 반환하고 있었다.

 

Integer.parseInt()

 

Integer.valueOf()

 

또한, Wrapper Class의 AutoBoxing이 도입된 이후로 parseInt()와 valueOf()는

사용성에 있어서는 차이점이 거의 없어졌다고 발표되었다.

 

결론적으로, 두 가지 메서드 중 어떤 것을 사용해도 무방하다.

 

 

이제, 형 변환이 적용되었는지 확인해보자.

	// 변수 생성 및 형변환.
	String s = "10";
	int i1 = Integer.parseInt(s);
	int i2 = Integer.valueOf(s);
		
	// 생성한 변수 값 출력.
	System.out.println(s);
	System.out.println(i1);
	System.out.println(i2);
		
	System.out.println(); // 줄바꿈.
		
	// 형변환 후, 타입 확인 출력.
	System.out.println(s.getClass().getSimpleName());
	System.out.println(((Object)i1).getClass().getSimpleName());
	System.out.println(((Object)i2).getClass().getSimpleName());

 

출력 결과는 다음과 같다.

 

형 변환이 성공적으로 이루어졌다.

 

(사실, 변수 초기화 단계에서 에러를 내뿜지 않고

대입되는 것에서부터 변환된 것을 확인할 수 있다)

 

 

 

· int => String 변환

	int i = 10;
		
	String s1 = Integer.toString(i);
	String s2 = String.valueOf(i);

 

Integer 클래스의 toString() 이나 String 클래스의 valueOf() 메서드를 사용한다.

 

마찬가지로, 둘 중 어떤 것을 사용해도 무방하다.

(String.valueOf() 메서드는 Integer.toString() 메서드를 포함하고 있다)

 

 

이제, 형 변환이 적용되었는지 확인해보자.

	// 변수 생성 및 형변환.
	int i = 10;
	String s1 = Integer.toString(i);
	String s2 = String.valueOf(i);

	// 생성한 변수 값 출력.
	System.out.println(i);
	System.out.println(s1);
	System.out.println(s2);
		
	System.out.println(); // 줄바꿈.
		
	// 형변환 후, 타입 확인 출력.
	System.out.println(((Object)i).getClass().getSimpleName());
	System.out.println(s1.getClass().getSimpleName());
	System.out.println(s2.getClass().getSimpleName());

 

출력 결과는 다음과 같다.

 

마찬가지로, 형 변환이 성공적이다.

 

 


 

● 정수 타입 int <=> 실수 타입 double

 

도입부에서 간단히 설명했지만, 정수와 실수의 변환 과정을 자세히 살펴보자.

	// 변수 초기화 및 형 변환.
	double d1 = 10.5;
	int i = (int)d1;
	double d2 = (double)i;
		
	// 변환 전후 변수 출력.
	System.out.println(d1);
	System.out.println(i);
	System.out.println(d2);
		
	System.out.println(); // 줄바꿈.
		
	// 형 변환 후, 타입 확인 출력.
	System.out.println(((Object)d1).getClass().getSimpleName());
	System.out.println(((Object)i).getClass().getSimpleName());
	System.out.println(((Object)d2).getClass().getSimpleName());

 

원시 타입 자료형 간의 변환은 별다른 메서드가 필요 없다.

 

위와 같이, (자료형)변수 와 같은 문법으로 작성해주면 된다.

 

 

10.5 라는 실수 값을 정수 타입 변수 i에 형변환 & 초기화하고,

정수 i를 다시 실수 타입 변수 d2에 형변환 & 초기화하고 출력했다.

 

 

출력 결과는 다음과 같다.

 

형 변환이 성공적으로 이루어졌다.

또한, 실수 => 정수로 변환될 때, 소수점이 버려지는 모습을 확인할 수 있다.

 

 

확실하게 인지해야 할 것은, '반올림' 따위는 신경 쓰지도 않는 것이다.

소수점 단위는 무조건 '버림' 처리된다.

	// 변수 초기화 및 형변환.
	double d1 = 10.1;
	double d2 = 10.9;
	int i1 = (int)d1;
	int i2 = (int)d2;
		
	// 변수 출력.
	System.out.println(i1);
	System.out.println(i2);

 

실수 10.1과 10.9를 정수로 변환하고 출력하는 예제다.

 

출력 결과는 다음과 같다.

 

10.1 과 10.9 둘 다, 무조건 10으로 변환되는 것을 알 수 있다.

 


 

- 범위를 초과하는 변환

 

 

우리가 흔히 사용하는 정수 타입 int의 범위는 약 -21억 ~ 21억 이다.

정확히는 -2,147,483,648 ~ 2,147,483,647 이다.

 

실수 타입 double 의 범위는 ±1.7E308(1.7의 308승)이다.

 

결론적으로, double의 범위가 int의 범위보다 크다.

 

 

그렇다면, int의 범위를 초과하는 실수를 int로 변환하면 어떻게 될까?

 

	// 변수 초기화 및 형변환.
	double d1 = 2147483648.123;
	double d2 = 1234567890123456.123;
	int i1 = (int)d1;
	int i2 = (int)d2;
		
	// 변수 출력.
	System.out.println(i1);
	System.out.println(i2);

 

int의 최대 범위보다 1.123이 큰 실수 타입 변수 d1,

약 1000조 단위인 실수 타입 변수 d2 를 선언하고,

 

각각 다른 정수 타입 변수에 담아서 초기화 후 출력하였다.

 

 

출력 결과는 다음과 같다.

 

서로 값이 다른 실수를 각각 변환 했지만,

2개의 변수 모두 int의 최대 범위를 출력하였다.

 

 

*위 처럼, 각 자료형의 저장 범위를 넘어가는 경우에는

오버플로우(overflow = 넘쳐 흐른다)가 발생할 수 있다.

 

실수 <=> 정수 변환 과정에서의 오버플로우로 인해

넘쳐 흘러 담지 못한 값들은 버려지는 것을 확인할 수 있다.

 

소수점 이하 단위를 '버림' 처리하는 것과 비슷한 맥락인 것 같다.

 

 


 

● 정수 타입 <=> 정수 타입

 

정수 타입에도 4가지 종류가 있다.

(byte, short, int, long)

자바의 자료형 - oscarstory

 

위 4가지 종류를 구분하는 기준은

포함하는 정수의 범위와 그에 따른 크기로 볼 수 있다.

 

 

그렇다면, 위에서 진행했던 실수와 정수의 변환 과정처럼,

같은 정수 타입을 포함하지 못하는 범위로 변환하면 어떻게 될까?

 

	// 변수 초기화 및 형변환.
	int i1 = 127;
	int i2 = 128;
	int i3 = 256;
	byte b1 = (byte)i1;
	byte b2 = (byte)i2;
	byte b3 = (byte)i3;
		
	// 변수 출력.
	System.out.println(b1);
	System.out.println(b2);
	System.out.println(b3);

 

byte 타입의 최대 범위인 127과,

범위를 초과하는 정수 2개를 각각 형변환 후 초기화했다.

 

 

출력 결과는 다음과 같다.

 

위에서 진행했던 double => int 의 오버플로우 결과와는 달랐다.

 

 

정수 타입끼리 변환 후 오버플로우가 발생했을 때는

해당 타입의 최대값으로 표기되지 않고,

 

최대값이 넘어가는 시점에서 최소값부터 다시 카운트 된다는 것을 알 수 있었다.

(byte 타입의 범위인 -128 ~ 127 을 한 사이클로 취급)

 

 

 


 

 

문자열과 기본 타입의 형 변환부터, 기본 타입들 간의 형 변환까지 알아보았다.

 

어떻게 보면 정말 기본적인 내용들이지만,

그만큼 잊혀지거나 실수하기 쉬운 부분이라고 생각한다.

 

 

복습을 하며 이런저런 생각으로 여러 시도를 하다가,

오버플로우라는 새로운 개념 또한 알게 되어 흥미로웠다.