오늘은 코틀린의 기본 자료형을 소개하고 코틀린의 자료형들이 자바와 비교했을 때 어떻게 다른지 비교해보겠습니다.
코틀린의 자료형(Basic Types)
코틀린의 공식문서를 보면 다음과 같이 자료형의 설명을 시작합니다.
모든 변수가 멤버 함수 또는 프로퍼티를 사용할 수 있다는 점에서 코틀린의 모든 것은 객체입니다.
특정 유형이 런타임 시 원시 값으로 최적화된 내부 표현을 갖지만, 사용자에게는 일반 클래스 처럼 나타나고 작동합니다.
💡여러분은 공식문서에서 설명하는 내용 중 다음과 같은 2개의 질문에 대답할 수 있나요?
- 왜 자료형 설명에서 느닷없이 코틀린의 모든 것이 객체라고 설명하는 걸까요?
- 왜 모든 유형이 아닌 특정 유형만 런타임 시 원시 값으로 최적화된 내부 표현을 갖게 될까요?
지금은 대답하지 못해도 괜찮습니다. 하지만 곧 대답할 수 있게 될겁니다. 이제 천천히 이해해보는 시간을 가져보도록 하죠.
코틀린의 기본 자료형
우선 우리는 코틀린의 기본 자료형에 대해 배워야 합니다.
사실 기본 자료형만 배우면 자료형은 모두 배우게 됩니다. 코틀린의 공식 문서에서 기본 자료형 외의 다른 자료형에 대한 설명은 없기 때문이죠.
- 코틀린 기본 자료형은 다음과 같습니다.
정수
타입 : 숫자 중 정수형에 해당합니다실수
타입 : 숫자 중 실수형에 해당합니다.부호가 없는 정수 타입
: 부호가 없는 정수형 입니다.불리안
타입 : 참/거짓을 표현 합니다.문자
타입 : 문자 1개를 표현 합니다.문자열
타입배열
타입
이제 차례대로 각 타입에 대해 간단하게 알아보겠습니다.
정수형(Integer Type)
- 정수는 크기가 다른 4가지의 타입이 있습니다.
- 보통
Int
타입을 가장 많이 사용하고, 값의 크기가 Int 범위를 넘어간다 싶으면Long
타입을 사용합니다. - 정리표
타입 비트 크기 최소값 최대값 Byte 8 -128 127 Short 16 -32768 32767 Int 32 -2,147,483,648 (-231) 2,147,483,647 (231 - 1) Long 64 -9,223,372,036,854,775,808 (-263) 9,223,372,036,854,775,807 (263 - 1)
실수형(Float Type)
- 실수는 크기가 다른 2가지의 타입이 있습니다.
- 보통
Double
타입을 가장 많이 사용합니다. - 부동소수점 IEEE 754 표준을 준수합니다.
- 정리표
타입 비트 크기 최소값 최대값 Float 32 1.4E-45 3.4028235E38 Double 64 4.9E-324 1.7976931348623157E308
부호가 없는 정수형
- 예를 들어, Int와 같은 정수형 타입 4바이트(32bit)에 할당된 비트 중 최상위 1 비트는 [
+
,-
]와 같은 부호를 할당하는 데에 쓰입니다. - 하지만 부호가 없는 정수 타입을 사용하면 1 비트 마저 숫자로 표현할 수 있습니다.
- 대신, -를 표현할 수 없으니 값의 범위는 0부터 시작됩니다. 대신 양수의 범위는 더 늘어나게 됩니다.
- 정리표
타입 비트 크기 최소값 최대값 UByte 8 0 255 UShort 16 0 65535 UInt 32 0 4,294,967,295 ULong 64 0 18,446,744,000,000,000,000
불리안형(Boolean Type)
- 불리안 타입은
true
와false
를 가진 타입입니다. - 불리안을
Boolean?
처럼 쓰면 null 값도 할당 가능합니다. - 값 또는 객체의
||(OR)
,&&(AND)
,!(NOT)
의 연산 등을 통해서도 Boolean 값을 할당할 수 있습니다. - 정리
var myTrue: Boolean = true
var myFalse: Boolean = false
var myBoolWithNull: Boolean? = null
println(myTrue || myFalse)
println(myTrue && myFalse)
println(!myTrue)
문자형(Character Type)
- 문자는 한 개의 문자를 나타내는 데, 사용하고, 홑 따옴표 ````를 사용하여 표현합니다.
- 보통은
\\
를 사용해서 특수 문자를 표현 하기도 하고 문자열 속 쌍 따옴표””
의 표현이나 줄바꿈 등에서 사용됩니다. - 또한 유니코드를 문자형 타입을 통해 표현할 수도 있습니다. 예)
‘\\uFF00’
var myChar = 'A'
문자열(String)
쌍 따옴표로 묶인 일련의 문자
를 문자열이라고 합니다.문자열 + 문자열
또는문자열 + 정수/실수/불리안/문자
의 연결은+
기호를 사용합니다.“”” “””
을 사용해서 여러 줄을 표현할 수도 있습니다.
var myInt = 1
var myDouble = 1.0
var myBool = true
var myChar = 'A'
var myStr = "승한"
println("myStr + myInt = ${myStr + myInt}") // 승한1
println("myStr + myDouble = ${myStr + myDouble}") // 승한1.0
println("myStr + myBool = ${myStr + myBool}") // 승한true
println("myStr + myChar = ${myStr + myChar}") // 승한A
var myLines = """
SELECT * FROM
MEMBER
"""
문자열이 초기화되면 그 이후엔 변경되지 않습니다.
- 이름을 나타내는
name
프로퍼티와 닉네임을 나타내는nickname
프로퍼티가 있습니다. - 우리는 두 문자열 프로퍼티를 더해서
이름_닉네임
의 형식으로 이름name의 값을 바꿔줄 겁니다. [3]
에서 값이 바뀐 name은 위에서 미리 만들어 둔[1]
name의 값의 수정이 이뤄진 것이 아니라name + “_$nickname”
의 값이 할당된 새로운 String 객체를 반환합니다.
var name = "승한" // [1]
var nickname = "핑키"
name = name + "_$nickname" // [3] 승한_핑키
- 💡 이 말이 어떤 뜻인지 아래의 예를 들어서 설명 해보겠습니다.
배열(Arrays)
- 배열은 동일한 타입 또는 그 하위의 타입을
고정된 사이즈
로 여러 개 보관하는 자료구조입니다. - 보통은 값의 추가가 편하고, 크기가 고정되지 않은 컬렉션의 리스트를 주로 사용합니다.(리스트와 배열은 다릅니다)
배열의 생성
- 배열은
arrayOf()
,Array<T>
,IntArray
,DoubleArray
,CharArray
등으로 생성할 수 있습니다. - 또한
emptyArray()
,arrayOfNulls()
를 사용해서 빈 배열, null로 초기화 된 배열을 생성할 수도 있습니다.
private var arrayOfInt = arrayOf(1, 2, 3) // arrayOf 사용 [1, 2, 3]
private var intArray = IntArray(3) { 0 } // IntArray 사용 [0, 0, 0]
private var intArrayGeneric = Array<Int>(3) { it + 1 } // Array<Int> 사용 [1, 2, 3]
private var arrayOfDouble = arrayOf(1.0, 2.0, 3.0) // arrayOf 사용 [1.0, 2.0, 3.0] Double 타입 추론
private var doubleArray = DoubleArray(3) { it + 1.0 } // DoubleArray 사용 [1.0, 2.0, 3.0]
private var doubleArrayGeneric = Array<Double>(3) { it + 1.0 } // Array<Double> 사용 [1.0, 2.0, 3.0]
코틀린 기본 자료형 타입 추론
자료형에 대한 설명이 끝났으니 자료형의 타입 추론은 어떻게 되는지 살펴보겠습니다.
정수형
초기화된 값에 .
이 붙지 않은 경우, 코틀린의 정수형 기본 추론은 **Int**
를 사용합니다.
값의 범위가 Int
를 벗어나면 Long
으로 추론됩니다.
리터럴 상수 L
을 숫자 끝에 명시하여 Long
타입임을 명시할 수 있습니다.
// "_"를 사용해서 100100,000,000,000처럼 사용할 수도 있습니다.
// var account: Long = 123_345_456_567_678
var myAccount = 123_345_456_567_678L // 타입을 지정하지 않아도 Int가 아닌 Long으로 추론됨
실수형
초기화된 값에 .
이 붙은 경우, 코틀린의 실수형 기본 추론은 **Double**
을 사용합니다.
리터럴 상수 f
를 숫자 끝에 명시하여 Float
타입임을 명시할 수 있습니다.
// var myHeight: Float = 178.7995
var myHeight = 178.7995f // 타입을 지정하지 않아도 Double이 아닌 Float으로 추론됨
부호가 없는 정수형
- 정수형과 마찬가지로 타입을 명시하지 않으면 크기에 따라
UInt
또는ULong
으로 추론됩니다. - 리터럴 상수
u
또는U
를 사용해서 부호가 없는 정수 타입임을 명시할 수 있습니다. - 리터럴 상수
uL
또는UL
을 사용해서 ULong을 명시할 수 있습니다.
불리안 / 캐릭터 / 문자열 / 배열 타입
Boolean
→ 변수에 초기화한 값이 true, falseChar
→ 변수에 초기화한 값이 홑 따옴표‘’
로 감싸지면String
→ 변수에 초기화한 값이 쌍 따옴표“”
로 감싸지면배열
→ 변수에 초기화한 값의 타입에 따라 배열의 타입도 함께 추론
자바의 자료형
이제 자바의 자료형에 대해서 아주 간단하게 알아봅시다
Primitive Type (원시형 타입)
- 정수(
byte
,short
,int
,long
) 실수(float
,double
), 문자(char
), 논리(boolean
) 처럼리터럴
계산을 위한 실제 값으로 저장합니다. - 코틀린과 다르게 첫글자가 대문자가 아닌 소문자인 것이 차이가 있습니다.
- 코틀린은 String은 기본형이지만, 자바의 String은 참조형입니다.
- 이렇게 앞 글자가 소문자인 자바의 기본 자료형 타입을 원시형 타입이라고 합니다.
- 메서드 호출, 다른 필드 접근 같은게 존재하지 않습니다.
리터럴이란?
- 데이터 그 자체를 의미합니다.
- 상수와 의미가 비슷하지만 프로그래밍에서의 상수는 값을 저장하면 변경할 수 없는 저장 공간이기 때문에 이를 구분하여 리터럴이라는 용어가 쓰입니다.
// 상수 리터럴
final int MY_BIRTHDAY = 980521;
Reference Type(참조형 타입)
- 객체(Object)의 번지를 참조하는 타입으로 배열, 열거(Enum), 클래스,인터페이스 타입을 말합니다.
- 사실상, 원시형 타입을 제외하고는 자바의 왠만한 타입이 모두 참조형 타입에 속한다고 보시면 됩니다.
BigInteger
- 정수형 중 가장 큰 크기를 가진 원시형
long
과 래퍼 클래스Long
은 모두 저장할 수 있는 최대값이 동일합니다. - 그래서 우리는 long 또는 Long 타입에
9,223,372,036,854,775,807
까지만 저장할 수 있습니다. - 하지만 이보다 더 큰 정수 값을 담아야 하는 경우가 생길 수 있습니다. 그럴 때 자바에서는
BigInteger
를 사용합니다. - BigInteger의 범위는 무려
무한
입니다.
Wrapper Class (래퍼 클래스)
- 래퍼 클래스의 종류
기본 타입 래퍼 클래스 byte Byte short Short int Integer long Long float Float double Double char Character boolean Boolean - 래퍼 클래스란?
- 원시형 타입의 데이터를 객체로 취급해야 하는 경우가 있습니다.
- 이런 경우를 해결하기 위해 자바에서는 래퍼 클래스를 제공합니다.
- 객체로 취급해야 되는 경우?
- 메소드에서 객체 타입만을 요구할 때
- 제네릭을 사용하는 클래스의 객체 생성을 위해(예 : 원시 타입은 컬렉션의 타입으로 사용하지 못하는 경우)
- 자료형에 null 초기화가 필요한 경우
- 박싱과 언박싱
- 기본 타입을 래퍼 클래스로 변환하는 것을
박싱
이라고 부릅니다. - 반대로 래퍼 클래스를 기본 타입으로 변환하는 것을
언박싱
이라고 부릅니다.
- 기본 타입을 래퍼 클래스로 변환하는 것을
차이 알아보기
- 이로써 코틀린과 자바의 자료형에 대해서 모두 알아보았습니다.
- 그럼 코틀린이 자바와 어떻게 상호작용 하는지 알아보겠습니다.
- 또한 2개의 질문에 대답해보겠습니다.
코틀린의 자료형은 1개, 자바의 자료형은 2개
- 코틀린의 자료형은 기본 자료형 하나만을 가지고 있습니다.
- 자바의 자료형은 기본 또는 원시형, 참조형 두 개를 가지고 있습니다.
- 코틀린의 모든 자료형을 클래스 구조로 설계 되어있고, 자바의 기본 자료형은 클래스가 아닙니다.
코틀린의 상호 운용성
- 코틀린은 자바 플랫폼에서 실행되도록 디자인되어 있으며 자바와의 상호작용을 중요하게 여깁니다.
- 따라서 코틀린에서 선언된 변수나 자료형은 자바의 기본 자료형과도 상호 변환이 자유롭게 이뤄집니다.
왜 자료형 설명에서 느닷없이 코틀린의 모든 것이 객체라고 설명하는 걸까요?
- 먼저, 자바의 자료형은 기본 자료형이 될 수도, 래퍼 클래스가 될 수도 있습니다.
- 반대로 코틀린은 오로지 클래스로 이루어진 자료형만을 가지고 있습니다.
- 따라서 자바에서 나눠졌던
Primitive Type
+Reference Type
을 코틀린에서는 클래스 구조인 객체로 설계해놓았기 떄문에 모든 것을 객체라고 부르는 것 입니다.
왜 모든 유형이 아닌 특정 유형만 런타임 시 원시 값으로 최적화된 내부 표현을 갖게 될까요?
- 특정 유형만 원시 값으로 내부 표현을 가진다는 뜻은 코틀린의
Int
타입이 JVM 런타임 시 자바의int
가 된다는 뜻 입니다. - 제 생각이지만 자바의 기본 자료형으로 바뀌게 되는 기준은 대략
null을 허용하지 않는가
,제네릭을 사용하지 않는가
에 따라 달라지게 되는 것 같습니다. - 반대로 코틀린의
Int?
는 Int에 정수형 또는 null이 들어갈 수 있음을 표현합니다. - 따라서 디컴파일된 자바 코드에서는 null 초기화가 가능한 래퍼 클래스
Integer
를 사용해야 합니다. 자바의 기본형인 int 타입은 null로 초기화를 할 수 없기 때문입니다. - 디컴파일을 통해 차이를 더 알아보겠습니다.
코틀린 코드
private var intValue: Int = 10
private var intValueWithNull: Int? = 10
private var stringValue: String = "문자열!"
private var stringValueWithNull: String? = "문자열!"
private var intArray = IntArray(3) { it + 1 }
private var intArrayOfInt = intArrayOf(1, 2, 3)
private var arrayOfInt = arrayOf(1, 2, 3)
private var intArrayGeneric = Array<Int>(3) { it + 1 }
private var doubleArray = DoubleArray(3) { it + 1.0 }
private var doubleArrayOfInt = doubleArrayOf(1.0, 2.0, 3.0)
private var arrayOfDouble = arrayOf(1.0, 2.0, 3.0)
private var doubleArrayGeneric = Array<Double>(3) { it + 1.0 }
자바 코드
private int intValue = 10;
private Integer intValueWithNull = 10;
private String stringValue = "문자열!";
private String stringValueWithNull = "문자열!";
private int[] intArray;
private int[] intArrayOfInt;
private Integer[] arrayOfInt;
private Integer[] intArrayGeneric;
private double[] doubleArray;
private double[] doubleArrayOfInt;
private Double[] arrayOfDouble;
private Double[] doubleArrayGeneric;
// 각 배열 초기화
- 정리표
코틀린 타입 null 허용 여부 디컴파일된 자바 타입 null 허용 여부 기본자료형 X int X 기본자료형? O Integer O 코틀린 타입 제네릭 사용 디컴파일된 자바 타입 제네릭 사용 기본자료형Array X int[] X Array<기본자료형> O Integer[] X - 코틀린의 기본 자료형중 String을 제외한 모든 기본 자료형은 JVM 런타임 시 자바 기본형 타입이 됩니다.
- 단,
?
와 같이 null 허용 연산자가 붙거나, 제네릭 클래스의 내부 타입으로 선언되면 래퍼 클래스가 됩니다. - 배열은 기본자료형 배열인 ByteArray, ShortArray, IntArray, .. CharArray 등의 경우 자바 기본형 타입의 배열(예:
int[]
)이 됩니다. - 배열이 Array 처럼 타입을 제네릭으로 갖는 경우, 래퍼 클래스의 배열(예 :
Integer[]
)이 됩니다.
자바의 원시형은 Stack 영역에 생성되지만, 참조형은 Heap영역에 생성된다.
JVM의 런타임 데이터 영역과, 설명에 필요한 Stack과 Heap에 대해서만 우선적으로 살펴봅시다.
JVM Runtime Data Area
- JVM이 프로세스로써 수행되기 위해 OS로부터 할당받는 메모리 영역이다. 저장 목적에 따라 각 영역이 나눠집니다.
메소드 영역
- .class 파일이 메모리 공간에 올라가면 초기화되는 대상을 저장합니다.
- 정적 필드와 클래스 구조만 가지고 있습니다.
- 필드의 정보 : 멤버 변수, 데이터 타입, 접근 제어자 정보
- 메소드 정보 : 메소드 이름, 리턴 타입, 매개변수, 접근 제어자 정보
- 타입 정보 : Class인지 Interface인지, 타입 속성, 슈퍼 클래스의 이름
- 힙 영역
- new 명령어로 생성된 클래스와 인스턴스 변수, 객체가 저장되는 구역입니다.
- 스택 영역
- 자바의 기본 자료형 타입을 생성할 때 저장하는 공간입니다.
- 임시적으로 사용하는 변수 또는 정보가 저장됩니다.
- 자료형에 따라 스택 영역에 저장되는 방식이 다릅니다.
- 기본형 타입의 경우 → 스택 영역에 직접
값
을 가집니다. - 참조형 타입의 경우 → 힙 영역 또는 메소드 영역의 객체 주소를 가집니다.
- 기본형 타입의 경우 → 스택 영역에 직접
코틀린은 자바의 JVM을 공유하는 언어입니다. JVM의 런타임 데이터 영역 중 스택 영역은 자료형에 따라 데이터가 저장되는 방식이 다릅니다.
스택 영역의 특징 중 하나로, 변수의 크기가 미리 정해져 있다는 점이 있습니다.
코틀린은 클래스 기반의 자료형이지만 자료형 별로 비트 크기가 정해져 있는 것처럼 스택 영역에 저장됨을 암시할 수 있습니다.
이러한 저장 방식을 따르지 않고 코틀린의 모든 자료형을 런타임시 자바의 래퍼 클래스 형태로 바꿨다면 자바와의 상호작용성은 물론 메모리의 효율성과 최적화도 고려되지 않은 설계가 됩니다.
정리
- 코틀린의 자료형에 대해서 학습했습니다.
- 자바의 자료형에 대해서 학습했습니다.
- 코틀린의 기본 타입 추론에 대해서 알게 되었습니다.
- 코틀린의 자료형과 자바의 자료형을 비교하여 코틀린의 상호작용에 대해서 알게 되었습니다.
- 코틀린의 모든 것은 객체임을 이해했습니다.
- 런타임 시 기본 자료형으로 바뀌게 되는 이유에 대해 학습했습니다.