Coding Log


Kotlin

본 카테고리는 2017년 Android 공식 언어로 채택된 Kotlin에 관하여 다룬다.

Kotlin을 이용해 개발하는 Android는 추후 따로 다루기로 하고 언어 자체에만 집중한다.

참고 kotlin 공식 사이트

기본 자료형 - 정수 및 실수

Kotlin의 모든 타입은 객체로 표현되며, 모든 변수에서 멤버 함수와 변수를 호출할 수 있다.

일부 타입은 특별한 내부 표현식을 가진다.

참고 숫자, 문자, Boolean은 런타임에 Primitive처럼 보이지만 일반 클래스처럼 표현된다.

이번 포스팅에선 정수 및 실수를 나타내는 숫자(Numbers)에 대해 작성한다.

kotlin은 Java와 유사한 방식으로 숫자를 처리한다.

유사하다는 말처럼 Java와 완전히 같지는 않지만, 다른 방식으로 처리하는데 숫자에 대한 암시적이 변환이 없다가 한 예시가 되겠다.

Kotlin에서 표현할 수 있는 정수 및 실수는 아래와 같다.

타입비트 수
Double64
Float32
Long64
Int32
Short16
Byte8

Kotlin에서 Character는 Numbers로 취급하지 않는다.

Character는 다음 포스팅에서 다루겠다.

리터럴 상수

정수 리터럴

  • Decimals : 123
    • Long은 대문자 L 로 표기 : 123L
  • Hexadecimals : 0x0F
  • Binaries : 0b00001011

참고 Kotlin은 8진법(Octal)은 지원하지 않는다.

실수(부동 소수점) 리터럴

  • Doubles(기본값) : 123.5123.5e10
    • Float는 문자 f, F 로 표기 : 123.5f123.5F

밑줄(Underscore)을 이용한 숫자 표기법

Kotlin은 밑줄을 이용해 숫자의 가독성을 높일 수 있다.

아래는 그 예시다.

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

참고 언더스코어 표기법은 1.1버전부터 지원한다.

표현식

Java 플랫폼에서는 숫자를 JVM의 Primitive 타입형태로 물리적으로 저장한다.

만약 저장하려는 숫자가 nullable를 참조(Int?)하거나 Generic이 필요한 경우엔 Boxing처리하여 표현한다.

아래의 예시를 보자.

val a: Int = 10000
print(a === a) // Prints 'true'
val boxedA: Int? = a
val anotherBoxedA: Int? = a
print(boxedA === anotherBoxedA) // !!!Prints 'false'!!!

위의 boxedA와 anotherBoxedA를 보면 같은 a를 저장하지만 false가 출력되는 것을 볼 수 있다.

이는 숫자를 박싱했을 경우 참조시 동일성(identity)이 유지되지 않는 것을 말한다.

살짝 바꾸어서 아래 코드를 보자.

val a: Int = 10000
print(a == a) // Prints 'true'
val boxedA: Int? = a
val anotherBoxedA: Int? = a
print(boxedA == anotherBoxedA) // Prints 'true'

반면 값의 동등성(equality)은 유지되는 것을 확인할 수 있다.

참고 =====과 같은 비교 연산자는 추후 포스팅에서 다루겠다.

명시적 형변환(Explicit Conversions)

10과 100처럼 작은 범위를 가진 타입은 큰 범위를 가진 타입의 하위 타입이 아니다.

이는 표현이 다르기 때문인데, 만약 표현 범위에 따라 하위 타입이 된다고 했을 때 발생하는 문제점은 아래와 같다.

val a: Int? = 1 // A boxed Int (java.lang.Integer)
val b: Long? = a // implicit conversion yields a boxed Long (java.lang.Long)
print(a == b) // Surprise! This prints "false" as Long's equals() check for other part to be Long as well

값 a는 Integer로 박싱되어 있다. 이를 값 b에 Long으로 박싱할 경우 a == b 코드는 false를 출력하게 된다.

이는 Long의 equals()는 비교 대상인지 Long인지 검사하는 메소드이기 때문이다.

따라서 위와 같이 작업하는 경우 참조 동일성과 값의 동등성까지 보장할 수 없게 된다.

참고 위의 코드는 문제점을 설명하기 위한 코드로서, 실제로 컴파일되지는 않는다.

위와 같은 문제점으로 인해 작은 표현 범위의 타입을 보다 큰 범위를 가진 타입으로 묵시적으로 변환하지 않는다.

아래 코드를 보자.

val b: Byte = 1 // OK, literals are checked statically
val i: Int = b // ERROR

표현 범위가 더 큰 Integer로도 묵시적 형변환이 되지않고 에러를 발생시킨다.

따라서 표현 범위가 더 큰 타입으로 변환한다는 전제하에 명시적 형변환을 진행해야만 에러를 피할 수 있다.

val b: Byte = 1 // OK, literals are checked statically
val i: Int = b.toInt() // OK: explicitly widened

위와 같이 처리하면 된다.

Kotlin은 모든 정수 및 실수를 표현하는 numbers에 관하여 아래의 변환 메소드를 제공한다.

java.lang.Numberkotlin.Number
byte byteValue()toByte(): Byte
short shortValue()toShort(): Short
int intValue()toInt(): Int
long longValuetoLong(): Long
float floatValue()toFloat(): Float
double doubleValue()toDouble(): Double
-toChar(): Char

단 Kotlin은 java에서 지원하지 않는 toChar() 함수를 추가로 제공한다.

이 함수는 해당 숫자에 해당하는 문자를 반환한다.

참고 Oracle Java Number Document

단, 산술 연산은 각 타입에 대해 오버로딩되어있기때문에, 묵시적 형변환을 하더라도 거의 문제가 발생하지 않는다.

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

정수와 실수의 연산

Kotlin은 올바르게 선언된 클래스의 멤버인 Number에 대해 산술 연산자를 제공한다.

이 연산자들은 각 타입의 멤버로 정의되어 있다가 컴파일 시점에 해당 명령어로 최적화한다.

비트 연산의 경우 일반적인 언어처럼 비트 연산자를 사용하지않고 중위 표기법을 이용하여 구현한다.

아래 코드가 그 예시다.

val x = (1 shl 2) and 0x000FF000

비트 연산자의 목록은 아래와 같다.

JavaKotlin동작
<<shl(bits)좌측 시프트
>>shr(bits)우측 시프트
>>>ushr(bits)부호없는 우측 시프트
&and(bits)AND 비트 연산
|or(bits)OR 비트 연산
^xor(bits)XOR 비트연산
~inv역(inverse) 비트연산

참고 Kotlin의 비트 연산은 Int와 Long만 가능하다.

참고 Oracle Java Operators Document

실수(부동소수점)의 비교 연산

실수 연산의 종류는 아래와 같다.

동일성 검사(Equality checks)

  • a == b
  • a != b

비교 연산자(Comparison operators)

  • a < b
  • a > b
  • a <= b
  • a >= b

범위 예시화와 범위 검사(Range instantiation and range checks)

  • a..b
  • x in a..b
  • x !in a..b

피연산자 a와 b가 FloatDouble 혹은 Nullable 대응시 숫자와 범위에 대한 연산은 IEEE 754 표준에 따른다.

참고 즉 피연산자 타입이 선언 되었거나, 추론 되었거나 smart cast 의 결과물일 경우를 말한다.

참고 IEEE 754 wikipedia

단, 실수가 정적으로 초기화되지 않는 일반적인 사용법에 대한 지원을 하기 위해 equals와 compareTo 메소드를 사용할 수 있다.

나머지 Kotlin의 실수에 대한 내용은 아래 3가지다.

  • NaN은 그 자체인 NaN과 동등하다.
  • NaN은 POSITIVE_INFINITY을 포함한 모든 요소보다 크다.
  • -0.0은 0.0보다 작다.


DISQUS 로드 중…
댓글 로드 중…

트랙백을 확인할 수 있습니다

URL을 배껴둬서 트랙백을 보낼 수 있습니다