Binary World

기본 타입(Basic Type) 본문

개발자의 길/Kotlin

기본 타입(Basic Type)

모쿠 2019. 5. 28. 18:09

코틀린에서 모든 것은 객체다. 변수에 대한 멤버 함수와 호출할 수 있다는 의미에서 모든 것은 객체다. 

일부 유형은 특수한 내부 표현을 할 수 있다. 예를 들어, 숫자, 문자, 논리(boolean)는 런타임에서 기본 값(primitive value)으로 나타낼 수 있다. 하지만, 사용자에게는 보통의 클래스로 보인다. 이 섹션에서는 숫자, 문자, 부울, 배열 및 문자열과 같은 Kotlin에서 사용되는 기본 유형을 설명한다.

 

숫자(Numbers)

코틀린에서 숫자는 자바와 유사하게 다뤄지지만, 완벽히 같지는 않다. 예를 들어, 숫자에 대한 암묵적인 확장 변환이 없고, 상수가 같은 케이스에서 약간 다르다.

코틀린은 다음과 같은 타입 표현을 갖는다.(자바랑 같음):

Type Bit width
Double 64
Float 32
Long 64
Int 32
Short 16
Byte 8

참고) 코틀린에서 문자는 숫자가 아니다.

 

리터럴 상수(Literal Constants)

리터럴은 자료를 알 수 있는 표기 형식을 말한다. 아래는 정수 값에 대한 리터럴 상수의 종류다:

  • 십진법: 123
    • Long타입은 끝에 문자L을 붙임 : 123L
  • 16진법: 0x0F
  • Binaries: 0b00001011

참고: 8진법은 지원하지 않음.

부동 소수점을 위한 기존 표기법도 지원한다:

  • Doubles by default: 123.5, 123.5e10
  • Floats은 끝에 문자 f 또는 F를 붙임: 123.5f

 

숫자 리터럴에서 밑줄(Underscore)

밑줄을 사용하여 숫자 상수를 더 쉽게 읽을 수 있다.:

val oneMillion = 1_000_000
val creditCardNumber = 1234_5678_9012_3456L
val socialSecurityNumber = 999_99_9999L
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010

 

표현법(Representation)

자바 플랫폼에서, null 가능한 정수 참조 (예 : Int?)가 필요하지 않거나, generics이 포함되지 않는다면, 숫자는 JVM 기본 타입으로 물리적으로 저장된다.  후자의 경우, 숫자는 박싱 된다.

 

박싱 된 숫자는 특징을 보존할 필요가 없음을 참고:

fun main() {
    val a: Int = 10000
    println(a === a) 
    val boxedA: Int? = a
    val anotherBoxedA: Int? = a
    println(boxedA === anotherBoxedA)
}

/* 출력 */
true
false

반면에, 아래 표현은 같음을 표기한다.:

fun main() {
    val a: Int = 10000
    println(a === a) // 'true' 출력
    val boxedA: Int? = a
    val anotherBoxedA: Int? = a
    println(boxedA === anotherBoxedA) // 'false' 출력
}

/* 출력 결과 */
true
true

 

명시적 전환(Explicit Conversions)

서로 다른 표현법 때문에, 작은 타입은 큰 타입의 서브타입이 아니다.:

// 가상의 코드. 실제로 컴파일 하지 않음:
val a: Int? = 1 // 박싱된 Int (java.lang.Integer)
val b: Long? = a // 명시적 형변환 산출. 박싱된 Long (java.lang.Long)
print(b == a) // 이 출력은 "false"가 된다. Integer와 Long은 갖지 않다.

결과적으로, 더 작은 타입을 더 큰 타입으로 암묵적으로 변경할 수 없다. 즉, 명시적 변환 없이 Int 변수에 Byte 타입의 값을 할당할 수 없다. 

fun main() {
    val b: Byte = 1 // 리터럴 상수 체크됨
    val i: Int = b // 에러
}

숫자를 확장하기 위해 명시적 형 변환을 사용 가능하다.

fun main() {
    val b: Byte = 1
    val i: Int = b.toInt() // 명식적으로 확대되었기 때문에 가능
    print(i)
}

/* 출력 결과 */
1

모든 숫자 타입은 아래 변환을 지원한다.

  • toByte(): Byte
  • toShort(): Short
  • toInt(): Int
  • toLong(): Long
  • toFloat(): Float
  • toDouble(): Double
  • toChar(): Char

암시적 변환은 문맥상에서 유추된 타입으로 인해 자동으로 실행되고, 적절한 변환을 위한 산술적 연산이 오버로드 된다.:

val l = 1L + 3 // Long + Int => Long

 

연산(Operations)

코틀린은 숫자에 대한 산술적 연산자 표준 세트를 지원한다. 

비트 연산의 경우, 특별한 문자가 제공되진 않는다. 그러나, infix form이라 불리는 명명된 함수를 사용할 수 있다.

val x = (1 shl 2) and 0x000FF000

비트 연산 종류는 아래와 같다.(IntLong만 사용 가능)

  • shl(bits) – 왼쪽으로 비트 이동 (Java's <<)
  • shr(bits) – 오른쪽으로 비트 이동 (Java's >>)
  • ushr(bits) – 양수(unsigned) 오른쪽으로 비트 이동 (Java's >>>)
  • and(bits) – 비트 단위 and
  • or(bits) – 비트 단위 or
  • xor(bits) – 비트 단위 xor
  • inv() – 비트 단위 inversion(반전)

 

부동 소수점 숫자 비교

부동 소수점 비교 연산은 다음과 같다.:

  • 대등 체크: a == b and a != b
  • 비교 연산자: a < b, a > b, a <= b, a >= b
  • 범위 인스턴스화 및 범위 체크: a..bx in a..bx !in a..b

피연산자가 정적 부동 소수점 숫자 타입으로 선언되지 않은 경우(e.g Any, Comparable<...>), FloatDouble에 대해, 표준에 동의하지 않은 equalscompareTo 구현을 사용한다. 따라서 아래와 같은 함수를 사용한다.

  • NaN은 스스로 동일한 것으로 간주된다.
  • NaN 은 POSITIVE_INFINITY을 포함한 어느 요소보다 큰 값으로 간주된다.
  • -0.0 0.0보다 작은 것으로 간주된다.

 

문자(Characters)

문자는 Char 타입으로 표현된다. 숫자로 직접 대치할 수 없다.

fun check(c: Char) {
    if (c == 1) { // 양립할 수 없는 타입들
        // ...
    }
}

문자 리터럴은 작은따옴표로 구성된다: '1'. 특수 문자는 백슬래쉬(\)를 이용해서 표현한다.

아래의 Escape sequence가 지원된다.:

\t, \b, \n, \r, \', \", \\\$ 

다른 문자로 인코딩하기 위해선 유니코드 Escape sequence를 사용한다.: '\uFF00'

 

문자를 Int 숫자로 변경할 수 있다.:

fun decimalDigitValue(c: Char): Int {
    if (c !in '0'..'9')
        throw IllegalArgumentException("Out of range")
    return c.toInt() - '0'.toInt() // 숫자에 대한 명시적 변환
}

숫자와 마찬가지로, Null 가능 참조가 필요할 때, 문자가 박싱 된다. 원형태는 박싱 연산에 의해 보호받지 못한다.

 

부울(Boolean)

Boolean 타입은 논리를 나타내고, 두 개의 값을 갖는다: true, false

Null 가능 참조가 필요하면 박싱된다.

아래와 같은 빌트인(Built-in) 연산이 포함

  • || – 둘 중 하나만 참이면 참
  • && – 둘 다 참일 때 참
  • ! - 부정

 

배열(Arrays)

코틀린에서 배열은 Array 클래스로 표현된다. getset 함수(연산자 오버로딩 변환으로 인해 []로 변경된)와 size 속성 등, 유용한 멤버 함수를 갖고있다.:

class Array<T> private constructor() {
    val size: Int
    operator fun get(index: Int): T
    operator fun set(index: Int, value: T): Unit

    operator fun iterator(): Iterator<T>
    // ...
}

배열을 만들기 위해, arrayOf() 함수를 사용한다. arrayOf(1, 2, 3)으로 생성하면 배열 [1, 2, 3]이 생성된다. arrayOfNulls()은 null 요소들로 채워진 배열을 생성할 수 있다.

 

배열 크기를 얻는 Array 생성자를 이용할 수 있다. 또한, 인덱스를 가지는 각 배열 요소들의 초기 값을 리턴하는 함수를 이용할 수 있다.:

fun main() {
    // ["0", "1", "4", "9", "16"]을 요소로 갖는 문자열 배열 생성
    val asc = Array(5) { i -> (i * i).toString() }
    asc.forEach { println(it) }
}

/* 출력 결과 */
0
1
4
9
16

위에서 언급했듯이, [] 연산자는 get()set() 멤버 함수에 대한 호출을 의미한다.

 

참고 : 자바와 다르게, 코틀린에서 배열은 불변이다. 이것은 코틀린이 가능한 런타임 에러들을 방지하기 위해, Array <String>에서 Array <Any>로 할당할 수 없다는 의미이다. 

 

코틀린은 박싱 오버헤드가 없는 기본 타입 배열을 표현하는 특별한 클래스가 존재한다. ByteArray, ShortArray, IntArray 기타 등등이다. 이 클래스들은 Array 클래스와 상속관계는 없지만, 메서드와 속성들은 동일하다. 또한, 각각에 상응하는 팩토리 함수를 갖고 있다.:

val x: IntArray = intArrayOf(1, 2, 3)
x[0] = x[1] + x[2]

 

부호 없는 정수(Unsigned Integer)

코틀린은 다음과 같은 Unsigned 타입을 제공한다.:

  • kotlin.UByte: Unsigned 8-bit 정수, 0 ~ 255 범위
  • kotlin.UShort: Unsigned 16-bit 정수, 0 ~ 65535 범위
  • kotlin.UInt: Unsigned 32-bit 정수, 0 ~ 2^32 - 1 범위
  • kotlin.ULong: Unsigned 64-bit 정수, 0 to 2^64 - 1 범위

특별한 클래스(Specialized classes)

기본 타입과 마찬가지로, Unsigned 타입도 배열 표현에 상응하는 타입이 존재한다.:

  • kotlin.UByteArray: unsigned bytes 배열
  • kotlin.UShortArray: unsigned shorts 배열
  • kotlin.UIntArray: unsigned ints 배열
  • kotlin.ULongArray: unsigned longs 배열

 

리터럴(Literals)

Unsigned 정수를 사용하기 쉽게 하기 위해, 정수 리터럴에 특정 Unsigned 타입을 나타내는 접미사로 태그를 지정하는 기능을 제공한다.  

  • 접미사 uU는 unsigned 태그 리터럴이다. 정확한 타입은 예상 타입에 의해 결정된다. 만약 예상 유형이 제공되지 않는다면,  UInt or ULong은 리터럴 크기에 따라 결정된다.

 

val b: UByte = 1u  // UByte, 예상 타입 제공
val s: UShort = 1u // UShort, 예상 타입 제공
val l: ULong = 1u  // ULong, 예상 타입 제공

val a1 = 42u // UInt: 예상 타입 제공안됨, UInt로 맞춰짐
val a2 = 0xFFFF_FFFF_FFFFu // ULong: 예상 타입 제공안됨, UInt에 맞출 수 없음

 

문자열(Strings)

문자열은 String 타입으로 표현된다. 문자열은 불변성을 지닌다. 문자열 요소들은 인덱스 연산으로 접근할 수 있는 문자들이다: s[i]. 문자열은 for-loop로 반복할 수 있다.

fun main() {
val str = "abcd"
    for (c in str) {
        println(c)
    }
}

/* 출력 결과 */
a
b
c
d

+ 연산자를 사용해서 문자열을 연결할 수 있다. 문자열에 다른 타입을 연결하기 위해서는, 첫 번째 요소가 문자열로 표현되어야 한다.:

fun main() {
    val s = "abc" + 1
    println(s + "def")
}

/* 출력 결과 */
abc1def

 

문자열 리터럴(String Literals)

문자열 리터럴은 두 가지 유형이 있음: 

Escaped 문자열은 escaped 문자를 포함한 문자열이고, raw 문자열은 개행 문자와 임의의 문자를 포함할 수 있는 문자열이다. Escaped 문자열은 자바와 매우 흡사하다.

val s = "Hello, world!\n"

Escaping은 백 슬러쉬(\)를 사용하여 쉽게 사용 가능하다.

raw 문자열은 삼 따옴표(""")를 사용하는 것으로 정해진다. 개행 등 다른 escaping 문자들은 포함되지 않아야 한다.

val text = """
    for (c in "foo")
        print(c)
"""

trimMargin() 함수를 이용하여 공백을 제거

val text = """
    |Tell me and I forget.
    |Teach me and I remember.
    |Involve me and I learn.
    |(Benjamin Franklin)
    """.trimMargin()

 

문자열 템플릿(String Templates)

문자열은 템플릿 표현을 포함할 수 있다. 템플릿 표현은 달러 모양($)으로 시작하고 변수 이름으로 구성된다.:

fun main() {
    val i = 10
    println("i = $i") 
}

/* 출력 결과 */
i = 10

중괄호 안에 임의의 표현도 가능:

fun main() {
    val s = "abc"
    println("$s.length is ${s.length}") 
}

/* 출력 결과 */
"abc.length is 3"

템플릿은 raw 문자열과 escaped 문자열을 동시에 지원한다. 리터럴 $ 문자를 raw 문자열에 포함한다면, 아래와 같이 표현한다.

 

val price = """
${'$'}9.99
"""

'개발자의 길 > Kotlin' 카테고리의 다른 글

제어 흐름(Control Flow)  (0) 2019.05.28
패키지와 임포트(Packages and Imports)  (0) 2019.05.28
기본 구문(Basic Syntax)  (0) 2019.05.28
코틀린 개발환경 구축 및 출력  (0) 2019.05.27
Kotlin이란 무엇인가  (0) 2019.05.27
Comments