Kotlin의 특징은 모든 데이터 타입에서 멤버 변수와 프로퍼티를 사용 할 수 있다는 점입니다.
또한 어떤 데이터 타입들은 일반적인 클래스처럼 사용하지만 내부적으로 Primitive 타입(numbers, characters, boolean)과 같이 최적화됩니다.
Integer 타입
Kotlin에서 내장되어 있는 정수형 타입입니다.
데이터 타입 | 데이터 크기(bits) | 최소 값 | 최대 값 |
8 | -128 | 127 | |
16 | -32768 | 32767 | |
32 | -2,147,483,648 (-2^31) | 2,147,483,647 (2^31 - 1) | |
64 | -9,223,372,036,854,775,808 (-2^63) | 9,223,372,036,854,775,807 (2^63 - 1) |
명시적으로 데이터 타입을 지정하지 않은 변수가 선언되면 컴파일러는 입력된 값에 따라서 사용 할 수 있는 가장 작은 데이터 크기의 타입을 추론합니다.
이 때 자동으로 선택되는 데이터 타입의 최소 크기는 Int(64bits)입니다.
val a = 1
val b = 3000000000
val c = 1L
val d: Byte = 1
코드 | 비고 | |
a = 1 | 할당 할 수 있는 가장 작은 데이터 크기의 |
|
b = 3000000000 | 할당 할 수 있는 가장 작은 데이터 크기의 |
|
c = 1L | ||
d: Byte = 1 |
부동 소수점(Floating-point) 타입
Kotlin은 실수 표현을 위해 Float과 Double 타입을 지원합니다. 이 타입들은 IEEE 754 표준을 준수합니다.
데이터 타입 | 데이터 크기(bits) | 부호(bits) | 가수(bits) | 지수(bits) |
32 | 1 | 23 | 8 | |
64 | 1 | 52 | 11 |
Kotlin 컴파일러는 마침표(.)를 사용해 실수를 표현하는 데이터를 Double 타입으로 추론합니다.
Float 타입을 명시하지 않고 실수를 저장하려면 f 또는 F 키워드를 사용합니다. Float 타입은 소수부 7자리까지 표현하며 그 이상은 반올림됩니다.
val a = 3.14
val b = 3.14f
val c: Float = 3.14
코드 | 비고 | |
Line 1 | a = 3.14 | 실수는 항상 |
Line 2 | b = 3.14f | |
Line 3 | c: Float = 3.14 |
Kotlin과 다른 언어와의 차이점은 숫자를 표현하는 데이터 타입의 암묵적인 캐스팅을 방지하는 점입니다.
fun main() {
val a: Double = 123.456
val b: Float = 123.456f
val c: Int = 123
val d: Short = 456
funDouble(a)
funDouble(b)
funInteger(c)
funInteger(d)
}
fun funDouble(value: Double) { }
fun funInteger(value: Int) { }
코드 | 비고 | |
Double 타입 인자를 받는 함수에 Double 타입을 전달합니다. | ||
Double 타입 인자를 받는 함수에 Float 타입을 전달합니다. Kotlin은 숫자 타입의 암묵적인 캐스팅을 지원하지 않으므로 |
||
Int 타입 인자를 받는 함수에 Int 타입을 전달합니다. | ||
Int 타입 인자를 받는 함수에 Short 타입을 전달합니다. Kotlin은 숫자 타입의 암묵적인 캐스팅을 지원하지 않으므로 |
JVM에서의 숫자 표현
Kotlin의 숫자는 JVM에서 int double 등 Primitive 타입으로 저장됩니다.
단 Int?와 같은 Nullable 표현식을 사용하거나 Generic으로 지정된 경우 예외입니다. 이러한 Non-Primitive 숫자는 Java 클래스 Integer Double처럼 박싱(Boxing)됩니다.
따라서 서로 다른 Nullable 객체가 동일한 숫자를 참조하더라도 실제로는 서로 다른 객체가 생성된다는 점을 주의해야합니다.
val a: Int = 10000
val boxedA: Int? = a
val anotherBoxedA: Int? = a
println(boxedA === anotherBoxedA)
코드 | 비고 | |
Nullable 표현식 |
||
따라서 비교 결과는 |
JVM은 -128부터 127까지의 숫자에 대해 메모리 최적화를 진행합니다.
위의 예시와 동일하지만 이번에는 참조되는 숫자를 100으로 입력합니다. 출력 결과는 true로 JVM 메모리 최적화로 인해 동일한 값을 참조하는 것을 알 수 있습니다.
val b: Int = 100
val boxedB: Int? = b
val anotherBoxedB: Int? = b
println(boxedB === anotherBoxedB)
Kotlin에서 == 연산자는 값을 비교합니다. === 연산자는 객체의 주소를 비교합니다.
예시의 boxedB와 anotherBoxedB는 서로 다른 객체이지만 여전히 == 연산자는 true로 유효합니다.
val b: Int = 10000
val boxedB: Int? = b
val anotherBoxedB: Int? = b
println(boxedB == anotherBoxedB)
명시적인 숫자 타입 캐스팅
Kotlin에서는 숫자 타입 간에는 암묵적인 타입 캐스팅이 발생하지 않습니다.
따라서 아래와 같은 코드는 Kotlin에서 컴파일하지 못하고 Type mismatch 오류가 발생합니다.
var a: Byte = 1
var b: Int = a
대신 명시적으로 숫자 타입을 캐스팅 할 수 있습니다.
var a: Byte = 1
var b: Int = a.toInt()
코드 | 비고 | |
사실 대부분의 경우 문맥 상 데이터 타입이 추론되기 때문에 명시적인 캐스팅은 자주 쓰이지 않습니다.
val a = 1L + 3
// long type
println(a.javaClass.name)
숫자 타입에 대한 연산자
Kotlin은 산술 연산에 대한 표준 집합 + - * / %을 지원합니다.
산술 연산은 각 숫자 타입의 클래스에서 멤버 변수로 선언되어 있습니다. 따라서 C++와 같이 Operator overloading을 통해 연산자의 동작을 재정의 할 수 있습니다.
정수에 대한 / 연산은 항상 정수 결과를 만듭니다.
val x = 5 / 2
// println(x == 2.5)
println(x == 2)
코드 | 비고 | |
정수에 대한 division 결과로 Int 타입이 추론됩니다. | ||
정수 타입에는 |
마찬가지로 다음 예시에서는 Long 타입이 추론되므로 == 연산자로 Long 타입과 비교해야 합니다.
val x = 5L / 2
// println(x == 2.5)
// println(x == 2)
println(x == 2L)
부동 소수점 타입으로 추론하기 위해서는 toDouble() 또는 2.0처럼 실수 연산임을 명시합니다.
val x = 5L / 2.toDouble()
println(x == 2.5)
정리 및 복습
- 정수형 타입
ByteShortIntLong을 지원합니다. 부동 소수점 타입은FloatDouble을 지원합니다. - Kotlin의
숫자는 JVM에서 Primitive 타입으로 최적화됩니다. - 반면
Nullable 타입은 Non-Primitive 타입으로 박싱(Boxing)됩니다. - Kotlin의 숫자 타입 간에는 암묵적인 캐스팅이 발생하지 않습니다. 이와 같은 경우에는
toInt()처럼 명시적으로 캐스팅이 필요합니다.
'Kotlin' 카테고리의 다른 글
Kotlin 도큐먼트: 클래스 상속 (0) | 2023.11.27 |
---|---|
Kotlin 도큐먼트: 클래스 선언하고 객체 생성하기 (0) | 2023.11.27 |
Kotlin 도큐먼트: 기본 문법 살펴보기 (0) | 2023.11.24 |
IntelliJ IDEA에서 Kotlin 시작 (0) | 2023.11.24 |
Kotlin이란? Kotlin과 Java 언어의 비교 (0) | 2023.11.20 |