F.R.I.D.A.Y.

Kotlin: 04 자료의 비교와 변환 본문

DEV/Kotlin

Kotlin: 04 자료의 비교와 변환

F.R.I.D.A.Y. 2020. 3. 27. 23:22
반응형

 이번 시간에는 자료를 비교하고 특정 자료형을 다른 자료형을 변환하는 방법을 알아봅니다.

 

# 처음 프로그래밍을 접하는 분들께는 생소하고 어려울 수 있습니다.


알아두기

 이 포스트를 이해하기 위해서는 한 가지 짚고 넘어가야 할 것이 있습니다. 코틀린의 데이터 저장/관리 방법입니다.

데이터 저장/관리 방법

 대개 프로그래밍 언어에서는 두 가지 방식의 변수를 제공합니다. 일반 변수와 참조 변수가 그것입니다.

 

일반 변수

 일반 변수는 데이터를 저장한 공간을 직접 가리키는 변수입니다. 마치 사람마다 고유한 이름이 있는 것처럼 말이죠.

 

참조 변수

  데이터를 특정 공간에 저장하면, 그 저장된 공간을 가리키는 변수라고 볼 수 있습니다. 대표적으로 C++에서는 참조 변수를 아래와 같이 작성합니다.

int value = 130;       // 일반 변수
int& refValue = value; // 참조 변수

 value라는 공간을 가리키는 refValue라는 변수가 생성되었습니다.

코틀린의 저장/관리 방식

 코틀린의 경우에는 모든 변수가 참조 변수의 성격을 가집니다. 코드상에서는 모두 참조 변수의 성격을 가지지만, 컴파일러 최적화를 거쳐 일부는 일반 변수처럼 사용됩니다.

fun main(args:Array<String>){
    var num1 = 130
    var num2 = 130
    println("num1:$num1, num2:$num2")
    
    num2 = 140
    println("num1:$num1, num2:$num2")
}

 이 코드를 예시로 들어보겠습니다. 변수 num1과 num2는 아래와 같이 데이터를 가리킵니다.

num1과 num2가 130을 저장한 방법

 코틀린은 코드에 적힌 값들을 데이터 테이블[# 값을 모아두는 곳]에 저장합니다.

num2 = 140

 해당 코드가 실행되면 num2가 아래처럼 140이 저장된 공간을 가리키도록 변경됩니다.

num1이 130, num2가 140을 저장한 방법

 

비교하기

 

fun main(args: Array<String>){
    val str1:String = "Hello world"
    val str2:String = "Hello world"
    
    println(str1 == str2)
    println(str1 === str2)
}

 코틀린에서는 같은지를 판별하는 두 가지의 연산자가 존재합니다. 등호를 두 개 작성한 이중 등호와 세 개 작성한 삼중 등호입니다. 이 두 연산자 모두 값을 비교한다는 것에서는 같지만 그 쓰임이 다릅니다.

이중 등호(==)

 이중 등호는 일반적인 같음을 판단할 때 사용하는 연산자입니다. 단순히 숫자, 혹은 문자열을 비교할 때 사용할 수 있죠.

문자열 1 문자열 2 비교 결과
'Hello world!' 'Hello world!' true
'Bye, world!'[# C언어의 창시자 데니스 리치가 사망할 때 그를 애도하는 의미에서 사용한 글귀입니다. 언어의 시작을 Hello world로 시작했기에 그의 마지막을 비슷한 방식으로 장식한 것이죠.] 'bye, world!' false[# 대소문자도 구별합니다.]

삼중 등호(===)

 삼중 등호는 비교하는 값이 어디에 저장되어 있는지를 비교[# 값에 상관없이 위치를 비교하는 것이라고 하는데, 같은 위치라 하면 결국 같은 값이 들어있을 텐데 설명을 왜 이렇게 되어있는지 의구심이 들기도 합니다. 반례가 있을까요?]합니다. 삼중 등호를 설명하기 위해서는 이후에 나올 세이프콜이 나와야 합니다. 지금은 "세이프콜이란 문법도 존재하는구나" 하고 가볍게 알아두기만 하기 바랍니다.

fun main(args:Array<String>){
	val a:Int = 130
	val b:Int = 130
	
	println(a === b)
	
	val c:Int? = a // 세이프콜
	val d:Int? = a // 세이프콜
	val e:Int? = c // 세이프콜
	
	println(c == d)
	println(c === d)
	println(c === e)
}

 이러한 코드가 있다고 생각해봅니다. 이 코드에서 다섯 개의 변수 a-e는 아래와 같이 값을 관리합니다.

 세이프콜 변수는 기본적으로 참조형 변수로 가집니다. 그래서 c-e 변수는 힙을 가리키도록 하고 힙 메모리 공간에 실제 데이터를 저장하죠. 그러나 변수 e의 경우에는 c 변수를 값으로 가집니다. 힙에는 이미 c가 가리키는 곳이 존재하므로 변수 e는 힙에 공간을 새로 만들지 않고 c 변수가 가리키는 곳을 가리키게 됩니다. 이를 C언어로 이해하면 아래와 같습니다. 물론 C언어의 특성과 코틀린의 특성은 다르기 때문에 완전히 같다고 말할 수는 없을 것입니다. 그러나 C언어를 배웠다면[# 특히 포인터 부분까지 공부했다면] 이해하는데 도움이 될 것입니다.

// C 언어
int a = 130;
int b = 130;

printf("%d\n", a == b);

int *c = &a;
int *d = &a;
int *e = c;

printf("%d %d %d\n", *c == *d, 
                     c == d,   // c와 d를 삼중 등호로 비교하는 코드. 주소를 비교
                     e == c);  // c와 e를 삼중 등호로 비교하는 코드. 주소를 비교
주의할 점

 비교하는 값이 만일 -128 ~ 127[# 부호가 있는 1바이트 자료형의 범위]이라면 삼중 등호를 이용해 비교하는 것이 제한됩니다. 1바이트로 표현 가능한 데이터는 캐시에 저장되어 위에서 비교한 코틀린 코드의 결과가 모두 참[# True]으로 나타납니다.

값의 변환(캐스팅)

 특정 값을 잠시 다른 자료형으로 변경하는 캐스팅 문법입니다. 코틀린에서 정수를 표현할 수 있는 자료형은 한 가지가 아니었습니다. 코틀린에서 값을 비교할 때는 자료형이 다르면 비교 연산자를 사용할 수 없습니다. 그래서 우리는 값의 변환에 대해 알아야 합니다.

암시적 형 변환

 다른 프로그래밍 언어를 보면 암시적 형 변환이라 불리는 것이 있습니다. 아래 C언어를 보겠습니다.

short num1 = 130;
int num2 = num1;

 num1의 값을 num2에 대입하려고 합니다. 자료형이 다르지만 컴파일에는 문제가 없습니다. 컴파일러가 형 변환을 해주기 때문입니다. 이를 암시적 형 변환이라고 합니다.

 이런 식의 암시적 형 변환이 코틀린에서는 제공되지 않습니다.

val num1:Int = 130
val num2:Long = num1 // ERROR: num1(Int)의 자료형이 num2(Long)의 자료형과 다릅니다.

 위와 같이 코드를 작성하면 두 번째 줄에서 오류를 터트리고 컴파일이 되지 않습니다. 그렇다면 코틀린에서는 어떻게 형 변환을 해야 할까요?

형 변환 메서드

 코틀린에서는 형 변환 연산자를 제공하지 않는 대신, 형 변환 메서드를 제공합니다.

메서드 명 설명 메서드 명 설명
toByte Byte 타입으로 변환 toFloat Float 타입으로 변환
toShort Short 타입으로 변환 toDouble Double 타입으로 변환
toInt Int 타입으로 변환 toChar Char 타입으로 변환
toLong Long 타입으로 변환    

 사용 방법은 아래와 같습니다.

fun main(args: Array<String>){
   val a:Int = 20
   
   println("${a.toLong()}")
}

 예를 들어 코드와 같이 toLong()을 이용하면 Int 자료형의 변수 a 이더라도, 출력할 당시의 a 값의 자료형은 Long입니다.

스마트 캐스트

 컴파일러가 개발자 대신 형 변환을 해주는 기능을 스마트 캐스트라고 부릅니다.

 

상위 자료형 이용하기

 코틀린에서는 각 자료형을 포함하는 상위 자료형이 있습니다. 마치 부모가 자식을 끌어안는 것처럼 말이죠.

자료형 설명
Any 모든 자료형의 부모(superclass)
Number Int, Long, Float 등 숫자형 자료형의 부모

 이들을 이용하면 굳이 형 변환을 하지 않더라도 필요한 값을 넣고 출력할 수 있습니다.

fun main(){
    var num:Number = 10
    println("num type is ${num.javaClass}")
    
    num = 10L
    println("num type is ${num.javaClass}")
}

 기존의 Int, Long 연산자를 사용했다면 컴파일 불가능했을 코드이지만, Number 자료형을 이용하니 가능합니다.

 

is를 이용한 자료형 검사

  코틀린에는 is 키워드 또한 존재합니다. 이 키워드는 각 자료형이 무엇인지 파악할 때 사용합니다. 또한 자료형을 is로 검사하게 되면, 해당 자료형을 변환하는 기능이 존재합니다.

 is 키워드는 주로 Any 자료형과 함께 사용합니다.

fun main(args: Array<String>){
	var tmp:Any = "Hello"	
	
	if(tmp is Int) println("$tmp")
	else println("not Int")
}

 위 코드를 설명하면 변수 tmp의 자료형이 Int로 변경될 수 있으면[#if-else if-else 구문에 대해서는 이후에 소개합니다.] tmp는 자료형이 Int로 캐스팅됩니다. 그러나 위 코드의 경우 Int로 변경할 수 없으므로 not Int가 출력됩니다.

 만일 Int가 아닐 때 True를 출력하려면 is 앞에 부정 논리 연산자인 "!"를 붙여주면 됩니다.

fun main(args: Array<String>){
	var tmp:Any = "Hello"	
	
	if(tmp !is Int) println("not Int")
	else println("tmp is type of Int")
}
as에 의한 스마트 캐스트

 as라는 키워드도 존재합니다. is 키워드와 비슷하지만, 변경 불가시 오류를 출력하지 않는 is와는 달리 as 키워드는 오류를 출력합니다.


 이후에 읽을 포스트에서 이 포스트에서 배운 내용이 보강될 것입니다. 소개되지 않는 문법을 이용해 설명하는 것을 최대한 배제하려고 하다 보니 같은 내용을 여러 포스트에 걸쳐 보강하고 보강하는 방식으로 글이 써지겠네요.

Next.

 

Kotlin: 05 연산자

이전 포스트들에서 몇 가지 연산자들을 먼저 사용해 보았습니다. 이번 시간에는 먼저 사용한 연산자들을 포함해 어떤 연산자들이 있고 그 기능은 무엇인지 알아봅니다. 산술 연산자 수학에서 산술 연산자라 하면..

pang2h.tistory.com

# index

728x90
반응형

'DEV > Kotlin' 카테고리의 다른 글

Kotlin: 06 세이프콜과 non-null 단정  (0) 2020.03.29
Kotlin: 05 연산자  (0) 2020.03.28
Kotlin: 03 출력하기  (0) 2020.03.24
Kotlin: 02 변수와 자료형  (1) 2020.03.23
Kotlin: 01 시작하기  (2) 2020.03.22
Comments