Note:이 연재는 'Do it! Kotlin Programming'에서 일부 내용이 연재되고 있습니다. 완전한 내용은 책을 참고해 주세요!
자, 이제 본격적으로 변수와 자료형에 대해 이야기할 시간입니다. 변수란 값을 넣을 수 있는 상자 정도로 비유할 수 있습니다. 다음은 변수와 변수에 담긴 값을 표현한 그림입니다. 변수는 상자이고 상자에 적힌 Int, String, Float은 상자에 담긴 값의 성질인 자료형을 의미합니다.
조금만 더 자세히 설명해 볼까요? 이름의 성을 나타내는 'kim' 이라는 값은 문자열(String), 나이를 나타내는 35라는 값은 정수(Int), 몸무게를 나타내는 55.6라는 값은 실수(Float) 형태의 자료형을 가집니다.
변수는 val, var라는 키워드를 이용하여 선언할 수 있습니다. 먼저 val과 var로 선언한 변수의 차이점을 짚고 넘어가겠습니다. val로 변수를 선언하면 최초 지정한 변수의 값으로 초기화하고 더 이상 바꿀 수 없는 읽기 전용 변수가 됩니다. var로 변수를 선언하면 최초 지정한 변수의 값이 있더라도 값을 바꿀 수 있습니다. 다음은 val로 선언한 변수에 30을 담고 var로 선언한 변수에 25를 담았다가 16으로 바꾼 것을 나타낸 그림입니다.
앞에서 설명했던 것처럼 변수를 선언하려면 val, var를 사용해야 합니다. 만약 여러분이 사용할 변수의 값이 변경되지 않아야 한다면 val을, 변경되어야 한다면 var를 이용하여 변수를 선언하면 됩니다. 다음은 변수를 선언한 예를 나타낸 그림입니다.
Tip:val로 변수를 선언해 놓고 변경되어야 할 때 var로 바꾸는 방법을 권장합니다. 이렇게 하면 버그 발생 확률을 많이 낮출 수 있습니다.
변경되지 않는 변수 username이 String 자료형으로 선언되었고 값은 "Kildong"이 할당되었습니다. 그런데 코틀린은 자료형을 지정하지 않고 변수를 선언하면 변수에 할당된 값("Kildong")을 보고 알아서 자료형을 지정할 수 있습니다. 바로 이것을 ‘자료형을 추론했다’라고 합니다.
즉, 다음과 같이 username 변수에 "Kildong"이라는 값만 할당해도 됩니다. "Kildong"은 문자열이므로 코틀린이 이 변수의 자료형을 String으로 지정합니다.
val username = "Kildong" // 코틀린이 자료형을 추론하여 username의 자료형을 String으로 결정
단, 자료형을 지정하지 않은 변수는 반드시 자료형을 추론할 값을 지정해야 합니다. 즉, '나중에 값을 대입해야지'라는 생각으로 자료형 없이 변수를 선언하면 안 됩니다. 값이 할당되지도 않은 변수의 자료형은 추론할 수 없기 때문이죠.
var username // 자료형을 지정하지 않은 변수는 사용할 수 없다
꼭 기억하세요. 값을 할당하지 않으면서 변수를 선언하려면 자료형을 반드시 지정해야 합니다.
이제 앞에서 배운 내용을 모두 정리한다는 기분으로 실습해 보겠습니다. KotlinProgramming 프로젝트에 chap02.section2이라는 패키지를 새로 만들고 다음과 같이 코드를 작성하고 실행해 보세요. number, language, secondNumber라는 변수를 선언한 다음 값을 할당하고 할당한 값을 출력합니다. 코드를 입력하며 어떤 변수에 자료형이 선언되어 있는지 눈 여겨 보세요.
val과 var 변수 선언 및 할당하기 | ValVar.kt |
package chap02.section2
fun main() {
val number = 10 // number 변수는 Int형으로 추론
var language = "Korean" // language 변수는 String으로 추론
val secondNumber: Int = 20 // sercondNumber 변수는 자료형을 Int형으로 명시적으로 지정
language = "English" // var 키워드로 선언한 변수는 값 다시 할당할 수 있음
println("number: $number")
println("language: $language")
println("secondNumber: $secondNumber")
}
Result |
number: 10 language: English secondNumber: 20
결과 화면이 제대로 출력되었나요? IntelliJ IDEA는 프로그램을 실행하기 전에 자료형이 지정되지 않은 변수가 어떤 자료형으로 추론되었는지 알려주는 기능이 있습니다. 자료형이 지정되지 않은 language 변수 이름을 클릭해 입력 커서를 놓고 Ctrl + Shift + P 를 눌러보세요. 추론된 자료형을 말풍선으로 알려줍니다.
다음은 변수 이름을 지을 때 주의해야 하는 내용을 정리한 것입니다. 이제 변수를 마무리하겠습니다.
Note: 카멜 표기법이란?
카멜 표기법이란 여러 단어로 된 변수 이름을 지정할 때 첫 글자는 소문자로 쓰고 각 단어의 첫 글자를 대문자로 써서 글자를 구분하는 방법입니다. 가령 책의 수를 저장하기 위한 변수로 numberOfBooks라고 이름을 지을 수 있습니다. 단어가 붙어 있는 모양이 낙타의 등과 같아서 붙여진 이름이지요. 보통 변수는 첫 번째 글자를 소문자로, 클래스(또는 인터페이스)는 첫 번째 글자를 대문자로 표기합니다.
지금까지 변수를 선언하는 방법에 대해 알아 보았습니다. 이번에는 코틀린의 자료형에 대해 하나씩 알아보겠습니다.
보통 프로그래밍 언어의 자료형은 기본형 자료형과 참조형 자료형으로 구분하며 코틀린은 참조형 자료형을 사용합니다. 그러면 기본형 자료형은 무엇이고 참조형 자료형은 무엇일까요?
Tip:앞으로 기본형 자료형은 기본형으로 참조형 자료형은 참조형으로 줄여 부르겠습니다.
기본형(Primitive Data Type)은 말 그대로 가공되지 않은 순수한 자료형을 말하며 프로그래밍 언어에 내장되어 있습니다. 참조형(Reference Type)은 객체를 생성하고 동적 공간에 데이터를 둔 다음 이것을 참조하는 자료형을 말합니다. 자바에서는 int, long, float, double 등 기본형과 String, Date와 같은 참조형을 모두 사용하지만 코틀린에서는 코딩할 때는 참조형만 사용하며 이것은 다시 코틀린의 성능 최적화에 따라 JVM에 실행하기 위해 코틀린 컴파일러에서 기본형으로 대체됩니다. 코딩할 때는 참조형의 최적화 부분을 신경 쓰지 않아도 됩니다.
다음은 자바에서 사용된 기본형과 참조형으로 선언한 변수의 코드 일부입니다.
int a = 77; // 기본형
Person person = new Person(); // 객체 참조형으로 person 객체를 위해 참조 주소(A12)를 가진다.
다음은 위의 코드가 실제로 저장되는 방식을 나타낸 그림입니다. 그림을 통해 기본형과 참조형이 메모리에 어떻게 저장되는지 설명해 보겠습니다.
기본형으로 선언한 변수 a는 주로 임시 메모리인 스택 메모리에 저장되며 값이 저장된 메모리의 크기도 고정되어 있습니다. 그림에서는 77이 저장된 부분을 참고하면 됩니다. 참조형은 스택에는 값이 아닌 참조 주소가 있습니다. 그림에서는 A12(예를 들기 위한 주소입니다)가 저장된 부분을 참고하면 됩니다. 그러면 참조형의 실제 객체는 어디에 있을까요? 실제 객체는 동적 메모리인 힙에 저장됩니다. 그림에서는 점선 화살표가 가리키는 도형을 참고하면 됩니다.
자바는 기본형과 참조형을 모두 사용할 수 있습니다. 하지만 기본형이 참조형보다 코드 수행 시간이 더 빠릅니다. 그러면 코틀린으로 만든 프로그램은 성능이 좋지 않을까요? 코틀린은 겉으로는 참조형을 사용하는 것 같지만 컴파일 과정을 거친 후 실제로는 기본형을 사용합니다. 코틀린 컴파일러가 자동적으로 최적화를 수행합니다. 따라서 코틀린에서는 기본형을 사용해야 할지 참조형을 사용해야 할지 고려할 필요가 없어 좋습니다.
정수 자료형은 양수, 음수, 0을 나타냅니다. 코틀린의 정수 자료형은 부호가 있는 것과 부호가 없는 것으로 나눌 수 있습니다. 다음은 부호가 있는 정수 자료형을 정리한 표입니다. 값의 범위에 따라 알맞은 정수 자료형을 사용하면 됩니다.
부호가 있는 정수 자료형
형식 | 자료형 | 크기 | 값의 범위 |
---|---|---|---|
정수 자료형 | Long | 8바이트(64비트) | -263~ 263-1 |
Int | 4바이트(32비트) | -231~ 231-1 | |
Short | 2바이트(16비트) | -215~ 215-1(-32,768 ~ 32,767) | |
Byte | 1바이트(8비트) | -27~ 27-1(-128 ~ 127) |
다음은 자료형을 지정하지 않은 변수에 여러 정수를 대입한 것입니다. 코드를 입력하고 Ctrl + Shift + P 를 눌러 추론된 자료형을 확인해 보세요.
val num05 = 127 // Int형으로 추론
val num06 = -32768 // Int형으로 추론
val num07 = 2147483647 // Int형으로 추론
val num08 = 9223372036854775807 // Long형으로 추론
정수를 표현할 때 숫자를 그냥 사용하면 10진수를 나타내지만 접미사나 접두사를 사용하면 다음과 같이 2진수나 16진수를 표현할 수 있습니다.
val exp01 = 123 // Int형으로 추론
val exp02 = 123L // 접미사 L을 사용해 Long형으로 추론
val exp03 = 0x0F // 접두사 0x를 사용해 16진 표기가 사용된 Int형으로 추론
val exp04 = 0b00001011 // 접두사 0b를 사용해 2진 표기가 사용된 Int형으로 추론
보통 숫자값은 Int로 추론되기 때문에 만일 좀 더 작은 범위의 정수 자료형인 Byte나 Short를 사용하기 위해서는 다음과 같이 직접 자료형을 명시해야 합니다.
val exp08: Byte = 127 // 명시적으로 자료형을 지정(Byte)
val exp09 = 32767 // 명시적으로 자료형을 지정하지 않으면 Short형 범위의 값도 Int형으로 추론
val exp10: Short = 32767 // 명시적으로 자료형을 지정(Short)
이번에는 음의 부호를 사용하지 않는 정수 자료형에 대해 알아봅시다. 양수만 표현할 수 있는 부호가 없는(unsigned) 정수 자료형은 부호가 있는 자료형보다 2배로 더 많은 양수를 표현할 수 있다는 장점이 있습니다. 하지만 부호가 없는 자료형은 코틀린 1.3 버전에서 실험적으로 도입한 기능입니다. 코틀린이 업데이트되면 이 자료형은 변경될 수 있으니 상업적 제품을 개발 할 때는 주의하세요! 다음은 코틀린에서 사용할 수 있는 부호가 없는 정수 자료형을 표로 정리한 것입니다.
부호가 없는 정수 자료형
형식 | 자료형 | 크기 | 값의 범위 |
---|---|---|---|
부호 없는 정수형 |
ULong | 8Bytes(64Bits) | 0 - 264- 1 |
UInt | 4Bytes(32Bits) | 0 - 232-1 | |
UShort | 2Bytes(16Bits) | 0 - 216(0 - 65535) | |
UByte | 1Bytes(8Bits) | 0 - 28(0 - 255) |
부호가 없는 정수 자료형을 사용할 때는 다음과 같이 식별자를 사용해 할당할 수 있습니다.
val uint: UInt = 153u
val ushort: UShort = 65535u
val ulong: ULong = 46322342uL
val ubyte: UByte = 255u
그런데 변수에 값을 할당하다 보면 어떤 경우는 값이 너무 길어서 읽기가 어려울 수 있습니다. 실생활에서는 큰 수의 자릿값을 쉼표(,)로 구분하죠? 코틀린에서는 언더바(_)를 사용합니다. 언더바는 값에 영향을 주지 않으므로 다음과 같이 원하는 위치에 아무데나 넣어 사용할 수 있습니다.
val number = 1_000_000
val cardNum = 1234_1234_1234_1234L
val hexVal = 0xAB_CD_EF_12
val bytes = 0b1101_0010
실수 자료형은 실수를 저장하기 위해 사용합니다. 다음은 실수 자료형을 표로 정리한 것입니다.
형식 | 자료형 | 크기 | 값의 범위 |
---|---|---|---|
실수형 | Double | 8바이트(64비트) | 약 4.9E-324 - 1.7E+308(IEEE754) |
Float | 4바이트(32비트) | 약 1.4E-45 - 3.4E+38(IEEE754) |
정수와 마찬가지로 실수도 자료형을 명시하지 않으면 Double형으로 추론합니다. 만약 Float형으로 지정하고 싶다면 간략하게 식별자 F를 실수 옆에 붙이는 방법을 사용해도 됩니다. 다음 코드를 참고하세요.
val exp01 = 3.14 // Double형으로 추론(기본)
val exp02 = 3.14F // 식별자 F에 의해 Float형으로 추론
실수의 개수는 무한합니다. 하지만 메모리 공간은 유한하죠. 그래서 메모리에 모든 실수를 표현하기는 어렵습니다. 이러한 메모리의 단점을 극복하기 위하여 실수를 표현할 때 부동 소수점(floating-point) 방식을 사용합니다. 부동 소수점 방식은 실수를 가수, 지수로 나누어 표현하는 방식입니다.
위의 그림은 수학, 컴퓨터에서 부동 소수점을 표현하는 방식을 나타낸 것입니다. 이 그림을 보며 부동 소수점이 무엇인지 설명해 보겠습니다.
예를 들어 0.0314는 3.14에서 소수점이 왼쪽으로 2칸만 이동하면 됩니다. 즉,3.14에 10-2를 곱하면 됩니다. 314는 소수점이 오른쪽으로 2칸만 이동하면 되죠. 숫자(3.14)는 그대로 두고 소수점만 둥둥 떠다니며 실수를 표현하고 있죠? 그래서 부동 소수점이라고 부르는 것입니다. 코틀린에서 소수점의 이동은 숫자 오른쪽에 e나 E와 함께 밑수인 10을 제외하고 지수만 적으면 됩니다. 예를 들어 3.14에서 왼쪽으로 소수점을 2칸 이동하려면 e-2나 E-2를, 오른쪽으로 소수점을 2칸 이동하려면 e2나 E2를 붙이면 됩니다. 다음은 부동 소수점 방식으로 실수를 표현한 것입니다.
val exp03 = 3.14E-2 // 왼쪽으로 소수점 2칸 이동, 0.0314
val exp04 = 3.14e2 // 오른쪽으로 소수점 2칸 이동, 314
코틀린은 IEEE754 표준의 부동 소수점 방식을 따르고 있습니다. 다음은 32비트와 64비트 방식으로 부동 소수점을 표현한 그림입니다. 그림을 보며 부동 소수점이 실수를 표현하는 방법과 오차에 대해 알아 보겠습니다.
'부호'에는 0과 1이 들어갑니다. 0은 양수를 1은 음수를 의미하죠. '지수'는 앞에서 공부한 실수의 지수 부분을 의미하며 32비트에서는 지수부분 8비트를 이용해 127(bias)을 기준으로 음수, 양수에 따라 지수를 더하거나 뺀 값을 사용합니다. 이것을 보수 표현으로 2진수로 나타냅니다. 예를 들어 -12.375(10)를 32비트의 float 로 표현하려면 먼저 음수이므로 부호비트는 1, 절댓값 12.375를 이진법으로 표현하면 1100.011(2)이 됩니다. 이것을 맨 앞자리만 1로 남기기 위해 정규화 합니다. 그러면 이진수로 1.100011 × 23 로 표현합니다. 이때 정수부는 항상 1이기 때문에 생략되고 100011에 대해서만 가수부인 23비트 내에서 표현합니다.
이제 23부분의 지수 부분을 봅시다. 양수 혹은 음수로 표현해야 하기 때문에 8비트로 구성하려면 -126에서 127까지 범위내에서 표현해야 합니다. IEEE 754에서는 기준값인 127을 사용해 127+3인 130을 이진법으로 표현한 10000010으로 지수부를 표현합니다.
공간의 제약으로 위의 방법으로는 모든 실수를 표현할 수 없습니다. 그리고 이런 방식을 이용하면 표현 비트의 제한 때문에 약간의 오차가 있기 때문에 사용 시 주의해야 합니다. 예를 들어 0.1은 사실 정확하게 0.1이 아니라 0.1에 아주 근접한 값입니다. 무슨 말일까요? 다음 코드를 살펴봅시다.
package chap02.section2
fun main() {
var num: Double = 0.1
for(x in 0..999) {
num += 0.1
}
println(num) //100.09999999999859
}
위의 코드는 부동 소수점으로 정의된 0.1을 1,000번 반복하여 더합니다. 예상되는 결괏값은 100이지만 실제로는 100이 아니라 100에 근접한 값인 100.09999999999859가 나옵니다. 십진수 0.1(10) 이라는 값은 이진수로 표현하면 0.0 0011 0011 0011...(2) 를 반복하는 순환소수가 됩니다. 따라서 제한된 가수부에 의해 Double에서 표현되는 가수부의 52비트를 넘어서는 부분이 잘려 나가서 표현됩니다. 그러니 부정확한 값으로 더해질 수 밖에 없는 것입니다. 부동 소수점을 사용할 때는 오차에 주의해야 합니다.
프로그램을 효율적으로 만들려면 메모리의 공간을 잘 활용해야겠죠? 그러려면 데이터 크기에 맞게 자료형을 지정하는 것이 중요합니다. 예를 들어 1000이 넘지 않는 정숫값을 저장하는 데에는 Int형보다 Short형을 사용하는 것이 더 좋습니다. Int형은 최대 2,147,483,647까지 저장할 수 있는 만큼 차지하는 메모리 공간이 Short형보다 더 크기 때문이죠. 데이터 크기에 맞게 자료형을 지정하려면 자료형의 최솟값과 최댓값을 파악하면 됩니다. 각 자료형의 요소인 MIN_VALUE와 MAX_VALUE를 사용해 Byte, Short, Int, Long, Float, Double의 최댓값과 최솟값을 나타내 봅시다.
정수형과 실수 자료형의 최솟값과 최대값 출력하기 | MinMax.kt |
package chap02.section2
fun main() {
println("Byte min: " + Byte.MIN_VALUE + " max: " + Byte.MAX_VALUE)
println("Short min: " + Short.MIN_VALUE + " max: " + Short.MAX_VALUE)
println("Int min: " + Int.MIN_VALUE + " max: " + Int.MAX_VALUE)
println("Long min: " + Long.MIN_VALUE + " max: " + Long.MAX_VALUE)
println("Float min: " + Float.MIN_VALUE + " max: " + Float.MAX_VALUE)
println("Double min: " + Double.MIN_VALUE + " max: " + Double.MAX_VALUE)
}
Result |
Byte min: -128 max: 127 Short min: -32768 max: 32767 Int min: -2147483648 max: 2147483647 Long min: -9223372036854775808 max: 9223372036854775807 Float min: 1.4E-45 max: 3.4028235E38 Double min: 4.9E-324 max: 1.7976931348623157E308
실행 결과는 각 자료형의 최댓값과 최솟값입니다. 이때 각 자료형에서 표현할 수 있는 최댓값이나 최솟값을 넘는 값을 할당하면 코딩 단계에서 에러로 잡아낼 수 있게 됩니다. 단, 비트 연산을 통해서 더해질 경우 2의 보수 표현에 의하여 부호가 바뀐 양수나 음수가 표현되 의도하지 않은 값으로 바뀔 수 있습니다.
Note: 2의 보수란?
만일 최대 값을 넘는 경우에는 어떻게 될까요? 예를 들어 Byte 자료형의 변수에 127이 들어 있을 때 여기에 1을 더하면 비트 연산에 의해 가장 상단의 비트인 부호 비트가 바뀌고 값은 -128이 됩니다. 이것을 그림으로 표현하면 항상 최대와 최소값이 환형처럼 붙어있다고 보면 쉬울 것 같습니다.
그러므로 최대값에서 하나를 더하면 부호가 바뀌게 되어 -128이 되는 것이죠. 컴퓨터는 음수를 표현하기 위해서 2의 보수라는 개념을 사용하고 있습니다. 다음 그림을 보면 좀 더 이해하기 쉬울 것입니다.
예를 들어 -6을 표현하려면 6의 2진값 0000 0110을 뒤집어 1111 1001로 만들고 거기에 다시 1을 더해 1111 1010이 됩니다. 이것이 2의 보수 표현법이며 컴퓨터 내부적으로 사용되는 기법입니다. 왜 이런 복잡해 보이는 기법을 사용할까요? 컴퓨터의 연산기는 사실 덧셈 기능의 회로만 있으며 덧셈 기능으로 뺄셈을 구현하기 위해서 만들어진 규칙입니다.
참, 거짓을 표현하는 논리 자료형은 조건을 검사할 때 많이 사용합니다. 코틀린에서 논리 자료형의 값은 true, false입니다. 다음은 논리 자료형을 표로 정리한 것입니다.
논리 자료형
형식 | 자료형 | 크기 | 값의 범위 |
---|---|---|---|
논리형 | Boolean | 1비트 | true, false |
논리 자료형은 흔히 검사 용도의 변수를 만들 때 사용합니다. 다음은 논리 자료형을 선언한 예입니다.
val isOpen = true // isOpen은 Boolean으로 추론
val isUploaded: Boolean // 선언만 한 경우 자료형(Boolean) 반드시 명시
문자 자료형(Char)은 문자를 표현하기 위해 사용하며 문자 자료형의 값은 작은따옴표(')로 감싸 표현합니다. 다음은 문자 자료형을 표로 정리한 것입니다.
형식 | 자료형 | 크기 | 값의 범위 |
---|---|---|---|
문자 | Char | 2바이트(16비트) | 0 ~ 215 ~ 1(\u0000 - \uffff) |
다음은 문자 자료형을 사용하여 변수를 선언한 예입니다.
val ch = 'c' // ch는 Char로 추론
val ch2: Char // 선언만 한 경우 자료형(Char) 반드시 명시
그런데 컴퓨터는 문자 자료형의 값을 저장할 때 문자 세트(아스키코드 표, 유니코드 표)를 참고하여 번호로 저장합니다. 예를 들어 컴퓨터에는 문자 a가 a로 저장되는 것이 아니라 65로 저장됩니다. 쉽게 말해 컴퓨터는 문자 a를 65로 이해합니다. 단, 코틀린에서 문자 자료형을 선언할 때만큼은 문자값으로 선언해야 합니다. 선언한 다음에는 문자 자료형에 숫자를 더하는 방식으로 다른 문자를 표현할 수 있습니다. 문자 자료형의 선언과 사용을 구분하여 기억하기를 바랍니다.
val ch = 'a'
println(ch + 1) // b
val chNum: Char = 65 // 에러! 숫자를 사용하여 선언하는 것은 금지
만약 정수형 자료형을 이용하여 문자 자료형을 선언하려면 어떻게 해야 할까요? 정숫값을 변환하는 함수 toChar를 이용하여 문자 자료형을 선언하면 됩니다.
val code: Int = 65
val chFromCode: Char = code.toChar() // code에 해당하는 문자를 할당
println(chFromCode) // 결과는 A
문자 자료형에는 1개의 문자만 저장할 수 있습니다. 만일 여러 문자가 나열된 문자열을 원하는 경우 문자열 자료형을 사용해야 합니다.
val ch4: Char = 'ab' // 에러! 두 개 이상의 문자는 담을 수 없다.
Note: 아스키코드와 유니코드란?
컴퓨터에서 보통 영문 위주의 문자를 표현할 때는 아스키코드(ASCII Code)를 사용합니다. 아스키코드는 1바이트로 문자를 표현하죠. 1바이트는 28(256)이므로 아스키코드로는 256개의 문자를 표현할 수 있습니다. 그런데 자바나 코틀린에서는 다양한 언어를 표현하기 위해 2바이트로 문자를 표현하는 유니코드를 사용합니다. 2바이트는 216(65536)이므로 65536개의 문자를 표현할 수 있습니다. 유니코드(Unicode)는 이스케이프 문자 \u와 16진수 4자리를 이용하여 문자를 표현합니다. 예를 들어 '한'이라는 문자는 변수에 \uD55C을 할당하면 됩니다.
val ch3 = '\uD55C' // 유니코드 표기법으로 '한'이라는 문자 지정
이제 문자열 자료형(String)에 대해 이야기 하겠습니다. 문자열 자료형은 문자 자료형에서 더 나아가 여러 문자를 배열하여 저장할 수 있는 자료형입니다. 그런데 왜 문자열 자료형은 따로 설명할까요? 문자형인 Char은 char과 같은 기본형으로 처리되지만 문자열 자료형은 기본형에 속하지 않는 배열 형태로 되어있는 특수한 자료형이기 때문입니다.
다음은 문자열 자료형을 선언한 것입니다. 다른 여러 자료형과 마찬가지로 문자열 자료형도 자료형의 이름을 지정하거나 추론 방식으로 선언할 수 있습니다. 마지막 코드 2줄은 문자열 자료형이 어떻게 저장되는지 확인하기 위하여 입력한 것입니다. 결괏값과 함께 문자열이 저장되는 방식도 알아보겠습니다.
문자열 선언 및 할당 | StringTest.kt |
package chap02.section2
fun main() {
var str1: String = "Hello"
var str2 = "World"
var str3 = "Hello"
println("str1 === str2: ${str1 === str2}")
println("str1 === str3: ${str1 === str3}")
}
Result |
str1 === str2: false str1 === str3: true
위의 코드는 문자열 변수를 3개 선언합니다. 다음은 문자열 str1
, str3
이 메모리에 저장된 것을 표현한 그림입니다.
위의 그림을 보면 str1, str3이 조금 특별한 방법으로 저장되어 있다는 것을 알 수 있습니다. str1, str3에는 같은 문자열이 저장되어 있습니다. 이런 경우에는 Hello를 메모리에 두 번 저장하는 것보다 이미 저장된 값을 활용하는 것이 효율적입니다. 코틀린은 힙 메모리 영역의 String Pool이라고 부르는 공간에 문자열인 Hello를 저장해두고 이 값을 str1, str3이 참조합니다. 결과적으로 str3의 참조 주소는 str1과 동일해 참조 비교를 위해 연산자 ===를 사용하면 true가 반환되는 것입니다. 이렇게 문자열 자료형은 String Pool이라는 공간을 이용해 사용되며 필요한 경우 재활용 됩니다.
변수의 값이나 표현식을 문자열 안에 넣어 출력하려면 어떻게 해야 할까요? 달러 기호($)와 함께 변수나 표현식을 사용하면 됩니다. 다음을 통해 $ 기호의 사용법을 알아보겠습니다.
var a = 1
val s1 = "a is $a" // String 자료형의 s1을 선언하고 초기화. 변수 a가 사용됨
문자열 안에 변수 a의 값을 사용할 수 있도록 $a을 사용했습니다. 그러면 $a가 변수 a의 값인 1로 대체되며 "a is 1"이 변수 s1에 할당됩니다. 만약 변수가 아니라 표현식을 문자열에 포함시키려면 중괄호({})를 사용하면 됩니다. 다음은 변수와 표현식을 문자열에 포함시킨 예제입니다.
문자열에 표현식 사용하기 | StringExpression.kt |
package chap02.section2
fun main() {
var a = 1
val str1 = "a = $a"
val str2 = "a = ${a + 2}" // 문자열에 표현식 사용
println("str1: \"$str1\", str2: \"$str2\"")
}
Result |
str1: "a = 1", str2: "a = 3"
예제의 결과를 보면 str2의 결과가 표현식에 의해 a에 2가 더해진 3 입니다. 그리고 이스케이프 문자인 \"을 사용해 큰따옴표를 문자열 안에 표현하고 있습니다. 이스케이프 문자는 역슬래시(\)를 붙여 표현하는 특수 문자 표기법 입니다. 예를 들어 큰따옴표와 $ 기호는 다음과 같은 이스케이프 문자로 표현합니다.
val special = "\"hello\", I have \$15"
println(special) // "hello", I have $15
Note: 중괄호를 사용해도 큰따옴표나 $를 표현할 수 있어요
다음과 같이 문자열 안에 ${' '} 를 이용해 큰따옴표나 $기호를 표현할 수 있습니다.\
val special2 = "${'"'}${'$'}9.99${'"'}" // "$9.99"
만약 문자열에 줄바꿈 문자, 탭 등의 특수문자가 포함된 문자열은 어떻게 표현해야 할까요? 코틀린은 이런 문자열도 손쉽게 나타낼 수 있습니다. 예를 들어 다음과 같은 문자열은 코틀린에서 어떻게 출력할까요?
목차의 정리 1장 코틀린의 소개 페이지 1 2장 변수와 자료형 페이지 10
위의 문자열에는 눈에 보이지 않는 줄바꿈, 탭, 공백 문자가 들어있습니다. 이런 문자들을 문자열에 포함시켜 출력하려면 """ 기호를 사용하면 됩니다. 그리고 이런 문자열을 형식화된 다중 문자열이라고 부릅니다. 형식화된 다중 문자열은 자주 사용하지는 않지만 보이지 않는 문자가 포함된 문자열을 한 번에 출력할 때 유용합니다. 다음을 통해 """의 사용 방법을 자세히 알아보겠습니다.
형식화된 코드 자체를 출력해 보기 | FormatedString.kt |
package chap02.section2
fun main() {
val num = 10
val formatedString = """
var a = 6
var b = "Kotlin"
println(a + num) // num의 값은 $num
"""
println(formatedString)
}
Result |
var a = 6 var b = "Kotlin" println(a + num) // num의 값은 10
formatedString
에 다중 문자열을 대입했습니다. """로 감싼 문자열에 있는 모든 내용이 그대로 출력되었죠? 단, $num
은 num의 값이 출력됩니다. 정말 줄바꿈 문자와 공백까지 모두 출력되었는지 다시 한 번 천천히 확인해 보세요.
자료형에 별명 붙이기
변수의 자료형이 복잡한 구조를 가지면 자료형에 별명을 붙일 수도 있습니다. 자료형에 별명을 붙이려면 typealias라는 키워드를 사용하면 됩니다. 다음은 String을 Username이라고 별명을 붙인 것입니다. 앞으로 배울 고차 함수와 람다식에서도 typealias를 많이 사용하므로 미리 익혀두고 넘어가겠습니다.
typealias Username = String // String을 Username이라는 별명으로 대체
val user: Username = "Kildong" // 이제부터 Username은 String과 같은 표현
이제 Username은 String과 같이 사용됩니다. 특정 자료형이나 클래스등에 별명을 붙여 좀 더 명확하게 이용하거나 아주 긴 선언의 클래스를 짧게 줄일 수도 있습니다. 이러한 기법들은 기본을 좀 더 배운 다음 활용 부분에서 살펴보겠습니다.
"어떤 것을 완전히 알려거든 그것을 다른 이에게 가르쳐라."
- Tryon Edwards -