Study(종료)/Kotlin 22.09.13 ~ 12.18

6-3. 특별한 클래스 사용하기 : 인라인 클래스(값 클래스)

Ski_ 2022. 10. 23. 20:55

6장

6-1. 특별한 클래스 사용하기 : 이넘(enun) 클래스

6-2. 특별한 클래스 사용하기 : 데이터 클래스

6-3. 특별한 클래스 사용하기 : 인라인 클래스(값 클래스)


6.3. 인라인 클래스(값 클래스)

프로그램에서 통화(돈)이라는 개념을 처리하고 싶을 때, 서로 다른 통화를 구분없이 섞어서 사용한다면

실수할 가능성도 많고, 복잡해진다.

이 경우 여러 통화를 서로 다른 클래스로 정의해 타입 시스템의 도움을 받아 오류를 줄일 수 있는데,

래퍼 클래스와 유틸리티 함수를 사용하면 이러한 방법을 사용할 수 있다.

class Dollar(val amount: Int) // amount = 센트
class Euro(val amount: Int) // amount = 센트

fun Dollar.toEuro() = ..
fun Euro.toDollar() = ..

새로운 통화를 만들 때 마다 래퍼 클래스를 생성해야 하므로 래퍼 클래스를 사용하면 런타임 부가 비용이 발생한다.

위 코드처럼 감싸야 하는 대상이 원시 타입이면 부가 비용 문제가 더 커진다.(런타임에서 실행 시간이 늘어난다)

이 문제 해결을 위해 코틀린은 인라인 클래스(inline class)를 도입했다.

원시 타입의 값과 마찬가지로 부가 비용이 없으므로 인라인 클래스를 값 클래스라고 부르기도 한다.

1) 인라인 클래스 정의하기

인라인 클래스를 정의하려면 inline class를 클래스 이름 앞에 붙여야 한다.

inline class Dollar(val amount: Int)
inline class Euro(val amount: Int)

인라인 클래스의 주생성자에는 불변 프로퍼티를 하나만 선언해야 한다.

inline class Dollar(val amount: Int, val x:String) 
// Error : Inline class must have exactly one primary constructor parameter

런타임에 클래스 인스턴스는 별도의 래퍼 객체를 생성하지 않고 이 프로퍼티의 값으로 표현된다.

 

인라인 클래스도 자체 함수와 프로퍼티를 포함할 수 있다.

inline class Dollar(val amount: Int){
    fun add(d: Dollar) = Dollar(amount + d.amount)
    val isDebt get() = amount < 0
}

fun main(){
    println(Dollar(15).add(Dollar(20)).amount) // 35
    println(Dollar(100).isDebt) // false
}

인라인 클래스도 프로그램 내 nullable-type 변수에 원시값을 대입하는 경우 암시적으로 박싱한다.

최적화를 위해 컴파일러는 가능하면 박싱하지 않은 값을 사용하려고 하지만,

박싱하지 않은 값을 사용할 수 없는 경우, 컴파일러는 인라인되지 않는 형태로 클래스를 사용한다.

인라인 클래스 인스턴스는 다른 어떤 타입의 값으로 캐스팅하지 않고도 정확히 상응하는 (프로퍼티) 타입의

값으로 사용될 수 있는 경우 인라인 될 수 있다 라는 규칙을 기억하자.

fun safeAmount(dollar: Dollar?) = dollar?.amount ?: 0

fun main(){
    println(Dollar(15).amount) // inline
    println(Dollar(15)) // Any? type > not inline
    println(safeAmount(Dollar(15))) // Dollar? type > not inline
}

2) 부호 없는 정수

부호 없는 타입을 기반으로 인라인 클래스로 작성된 부호 없는 정수 타입이 있다.부호 없는 정수 타입은 부호 있는 정수 타입의 이름 앞에 U를 붙여 사용할 수 있다.

	
    val unsignedByte: UByte = 1u // UByte : 0 ~ 255, 1byte
    val uShort: UShort = 100u // UShort : 0 ~ 65535, 2byte
    val uInt: UInt = UInt.MAX_VALUE // UInt : 4294967295(2^32-1), 4byte
    val uLong: ULong = ULong.MAX_VALUE // ULong : 18446744073709551615(2^64-1), 8byte

    println(uInt.toInt())
    
//    println(1u + 2) 
//    Error : Conversion of signed constants to unsigned ones is prohibited

 


마치며

그래도 이번장은 짧기도 하고 내용역시  부담이 많이 없었다.

사실 원래 스터디날이 기사 시험날이라 기사 공부하고, 그날은 좀 쉬고싶어서 스터디 참가를 못한다고 했는데.

카카오 서버가 터져버려서 아예 그날 스터디 진행을 못했다.

 

이번장이 부담이 없는 대신 다음장은 부담이 좀 많이 된다.

다음장은 컬렉션과 I/O 자세히 알아보기이고, 무려 60쪽이나 된다..

다 정리할 수 있을지는 모르겠지만, 할수 있는 만큼 최대한 해봐야겠다

 

반응형