본문 바로가기
  • 개발하는 곰돌이
Programming Language/Kotlin & Java

[Kotlin] Kotlin에서의 형변환과 스마트 캐스트(Smart Cast) feat. as, is

by 개발하는 곰돌이 2023. 1. 16.

개요

코딩을 하다보면 필연적으로 자료형을 변환해야 하는 경우가 생긴다. Java에서는 숫자형의 경우에는 변수의 자료형에 따라서 자동으로 형변환이 일어나서 저장되었고, 변수나 객체 앞에 (자료형)을 붙여주는 것으로 강제로 형변환을 할 수도 있었다. Kotlin도 마찬가지로 형변환이 가능한데 이에 대해 정리하고자 한다.


기본 자료형의 형변환

Kotlin의 기본 자료형들은 기본적으로 변수에 대입할 때 자동 형변환을 지원하지 않는다. 예를 들어, Java에서는 실수형에 정수형을 대입하거나 정수형에 문자형을 대입하려고 하면 자동으로 형변환이 일어나서 다음과 같은 코드가 모두 동작했다.

double a = 10;
boolean c = a == 10;	// true
int b = 123;
a = b;	// OK

하지만 Kotlin에서는 대입이나 비교 연산을 할 때 이러한 자동 형변환을 지원하지 않기 때문에 아래와 같이 상위 타입에 하위 타입을 대입하려고 하면 모두 에러가 발생한다.

var a = 10.0	// Double
val c = a == 10	// ERROR
var b = 123	// Int
a = b	// Double형에 Int형을 저장하려고 해서 ERROR 발생

이런 경우에 Kotlin에서는 형변환을 하고싶은 변수에 다음과 같은 to자료형() 함수를 사용하여 형변환을 해줘야한다.

  • toByte() : Byte 타입으로 변환
  • toUByte() : unsigned 타입인 UByte 타입으로 변환
  • toShort() : Short 타입으로 변환
  • toUShort() : unsigned 타입인 UShort 타입으로 변환
  • toInt() : Int 타입으로 변환
  • toUInt() : unsigned 타입인 UInt 타입으로 변환
  • toLong() : Long 타입으로 변환
  • toULong() : unsigned 타입인 ULong 타입으로 변환
  • toFloat() : Float 타입으로 변환
  • toDouble() : Double 타입으로 변환
  • toChar() : Char 타입으로 변환
  • toString() : String 타입으로 변환
  • toBigInteger() : BigInteger 타입으로 변환
  • toBigDecimal() : BigDecimal 타입으로 변환

이러한 Kotlin의 형변환 함수들은 기본 자료형들 사이에선 자료형을 가리지 않는다. 즉, Kotlin에서는 문자열도 위의 형변환 함수를 사용해서 정수나 실수, 또는 BigInteger나 BigDecimal로 바로 변환할 수 있다. Java에서는 문자열을 숫자 타입으로 변환할 때 Integer.parseInt()와 같은 Wrapper Class의 메소드를 사용해야 했고, BigInteger나 BigDecimal으로 변환할 때는 각 클래스의 생성자를 호출해야 했던 것에 비하면 상당히 간결해졌다고 볼 수 있다. 물론, 이 경우에 문자열을 변환하고자 하는 자료형으로 변환할 수 없다면 형변환 함수는 NumberFormatException을 던진다.

val a = "123456"
val b = "123.456"
val c = a.toInt()	// Int
val d = b.toDouble()	// Double
val e = b.toInt()	// NumberFormatException

객체들 사이에서의 형변환

Java에서 컬렉션에 여러 타입을 저장해야 하는 경우가 있어서 Object를 사용하면 그 값을 꺼내서 사용할 때 앞에 (자료형) 을 붙여서 명시적으로 형변환을 해줘야 했다. Kotlin에서도 이러한 명시적 형변환이 가능한데, 바로 as 키워드를 사용한다.

Map<String, Object> map = Map.of("key1", "value1", "key2", 2);
String a = (String) map.get("key1");
int b = (int) map.get("key2");
val map = mapOf<String, Any>(Pair("key1", "value1"), Pair("key2", 2))
val a = map["key1"] as String
val b = map["key2"] as Int

Java의 명시적 형변환과 다른 점이라면 Java에서는 형변환을 하고자하는 대상의 앞에 (자료형)을 선언했지만, Kotlin에서는 대상의 뒤에 as 자료형을 붙여야 한다는 점이다.


Kotlin의 스마트 캐스트(Smart Cast)

Kotlin은 스마트 캐스트라는 기능을 지원한다. 스마트 캐스트를 그대로 직역하면 똑똑한 형변환이라고 볼 수 있는데, 말 그대로 특정 상황에서 컴파일러가 알아서 똑똑하게 형변환을 해주는 기능이다. 예를 들어, Any를 값으로 갖는 Map에서 값을 가져왔을 때 그 값의 타입이 무엇인지 확인을 하고 그에 따라 다른 연산을 수행해야 하는 경우가 있다.

 

Java에서는 if문의 조건식에서 instanceof를 사용하여 객체의 타입을 검사하더라도 if문 블록 내부에서 명시적으로 형변환을 해줘야 해당 타입에 맞는 연산이 가능하다.

Map<String, Object> map = Map.of("key1", "value1", "key2", 2);
Object a = map.get("key1");
if (a instanceof String) {
    System.out.println(((String) a).length());
}

Kotlin에서는 이러한 경우에 is 키워드를 사용하여 타입을 확인하는데, if문의 조건식에서 is로 타입을 검사한 경우 해당 if문 블록의 내부에서는 따로 형변환을 해주지 않아도 자동으로 검사한 타입으로 취급하게 된다. if문 블록 내부의 코드에 접근이 가능하다는 것은 해당 타입인지 검사한 것을 통과했다는 의미이고, 이는 곧 그 객체가 검사한 타입이라는 의미므로 컴파일러가 스스로 판단을 한 것이다. 이러한 형변환을 스마트 캐스트(Smart Cast)라고 한다.

val map = mapOf<String, Any>(Pair("key1", "value1"), Pair("key2", 2))
val a = map["key1"]
if (a is String) {
    println(a.length)
}

실제로 위의 예시 코드에서 if 블록 내부의 a에 마우스를 올려보면 Kotlin에서는 스마트 캐스트가 되었다는 문구를 볼 수 있다.

Java는 스마트 캐스트를 제공하지 않는다.
Kotlin의 스마트 캐스트

이러한 스마트 캐스트는 when문에서 타입을 검사했을 때도 동일하게 제공된다.

댓글