Kotlin의 인라인 값 클래스(Inline Value Classes)
Kotlin의 인라인 값 클래스(Inline Value Classes)는 성능 최적화와 타입 안전성을 동시에 제공하는 강력한 기능입니다.
이 문서에서 인라인 값 클래스의 다양한 사용법을 살펴봅니다.
인라인 값 클래스 기본 (Inline Value Class Basics)
인라인 값 클래스는 단일 값을 래핑하여 보다 도메인 특화된 타입을 만들 때 유용합니다. 이는 값 기반 클래스의 하위 집합으로, 아이덴티티가 없고 오직 값만을 보유합니다.
value class Password(private val s: String)
// JVM 백엔드를 위한 선언
@JvmInline
value class Password(private val s: String)
인라인 클래스는 단일 프로퍼티를 가져야 하며, 이 프로퍼티는 주 생성자에서 초기화되어야 합니다:
val securePassword = Password("Don't try this in production")
런타임에서 securePassword
는 실제로 String
값만을 포함합니다.
멤버 (Members)
인라인 클래스는 일반 클래스의 일부 기능을 지원합니다:
@JvmInline
value class Person(private val fullName: String) {
init {
require(fullName.isNotEmpty()) {
"Full name shouldn't be empty"
}
}
constructor(firstName: String, lastName: String) : this("$firstName $lastName") {
require(lastName.isNotBlank()) {
"Last name shouldn't be empty"
}
}
val length: Int
get() = fullName.length
fun greet() {
println("Hello, $fullName")
}
}
fun main() {
val name1 = Person("Kotlin", "Mascot")
val name2 = Person("Kodee")
name1.greet() // 정적 메서드로 호출됨
println(name2.length) // 프로퍼티 게터가 정적 메서드로 호출됨
}
상속 (Inheritance)
인라인 클래스는 인터페이스를 구현할 수 있지만, 다른 클래스를 상속할 수는 없습니다:
interface Printable {
fun prettyPrint(): String
}
@JvmInline
value class Name(val s: String) : Printable {
override fun prettyPrint(): String = "Let's $s!"
}
표현 (Representation)
컴파일된 코드에서 인라인 클래스 인스턴스는 래퍼 또는 기본 타입으로 표현될 수 있습니다:
interface I
@JvmInline
value class Foo(val i: Int) : I
fun asInline(f: Foo) {}
fun <T> asGeneric(x: T) {}
fun asInterface(i: I) {}
fun asNullable(i: Foo?) {}
fun main() {
val f = Foo(42)
asInline(f) // 언박싱: Foo 자체로 사용됨
asGeneric(f) // 박싱: 제네릭 타입 T로 사용됨
asInterface(f) // 박싱: 타입 I로 사용됨
asNullable(f) // 박싱: Foo?로 사용됨 (Foo와 다름)
}
맹글링 (Mangling)
인라인 클래스를 사용하는 함수는 이름에 안정적인 해시코드가 추가되어 맹글링됩니다. 이는 플랫폼 시그니처 충돌을 방지합니다.
Java 코드에서 호출 (Calling from Java code)
Java 코드에서 인라인 클래스를 사용하는 함수를 호출하려면 @JvmName
어노테이션을 사용하여 맹글링을 수동으로 비활성화해야 합니다:
@JvmInline
value class UInt(val x: Int)
@JvmName("computeUInt")
fun compute(x: UInt) { }
인라인 클래스 vs 타입 별칭 (Inline classes vs type aliases)
인라인 클래스와 타입 별칭은 유사해 보이지만 중요한 차이가 있습니다:
typealias NameTypeAlias = String
@JvmInline
value class NameInlineClass(val s: String)
fun acceptString(s: String) {}
fun acceptNameTypeAlias(n: NameTypeAlias) {}
fun acceptNameInlineClass(p: NameInlineClass) {}
fun main() {
val nameAlias: NameTypeAlias = ""
val nameInlineClass: NameInlineClass = NameInlineClass("")
val string: String = ""
acceptString(nameAlias) // OK
acceptString(nameInlineClass) // 오류
acceptNameTypeAlias(string) // OK
acceptNameInlineClass(string) // 오류
}
인라인 클래스는 새로운 타입을 도입하는 반면, 타입 별칭은 기존 타입의 대체 이름일 뿐입니다.
인라인 클래스와 위임 (Inline classes and delegation)
인라인 클래스는 인터페이스를 통한 위임을 지원합니다:
interface MyInterface {
fun bar()
fun foo() = "foo"
}
@JvmInline
value class MyInterfaceWrapper(val myInterface: MyInterface) : MyInterface by myInterface
fun main() {
val my = MyInterfaceWrapper(object : MyInterface {
override fun bar() {
// 구현
}
})
println(my.foo()) // "foo" 출력
}
Kotlin의 인라인 값 클래스는 성능 최적화와 타입 안전성을 동시에 제공하는 강력한 기능입니다. 특히 원시 타입을 래핑하거나 도메인 특화 타입을 만들 때 유용합니다. 하지만 인라인 클래스의 사용은 신중하게 고려해야 합니다:
- 성능 이점: 불필요한 객체 생성을 줄여 성능을 향상시킬 수 있습니다.
- 타입 안전성: 기본 타입과 구분되는 새로운 타입을 생성하여 타입 안전성을 높입니다.
- API 설계: 도메인 특화 타입을 만들어 API를 더 명확하게 설계할 수 있습니다.
- 호환성: Java 코드와의 호환성을 고려해야 합니다.
- 제한사항: 인라인 클래스는 단일 프로퍼티만을 가질 수 있고, 다른 클래스를 상속할 수 없습니다.
인라인 값 클래스를 적절히 활용하면 코드의 표현력과 성능을 동시에 개선할 수 있습니다. 하지만 과도한 사용은 코드의 복잡성을 증가시킬 수 있으므로, 프로젝트의 요구사항과 특성을 고려하여 적절히 사용하는 것이 중요합니다.
Kotlin, 인라인클래스, 값클래스, 성능최적화, 타입안전성, JVM, 맹글링, 박싱, 언박싱, API설계, 코틀린문법, 안드로이드개발, 서버개발, 메모리관리, 코드최적화
'개발&프로그래밍' 카테고리의 다른 글
[Kotlin] 위임(Delegation) (0) | 2024.08.17 |
---|---|
[Kotlin] 객체 표현식과 선언(object expressions and object declarations) (0) | 2024.08.17 |
[Kotlin] 열거형 클래스(Enum class) (0) | 2024.08.15 |
[Kotlin] 중첩 클래스와 내부 클래스 (0) | 2024.08.15 |
[kotlin] 제네릭: in, out, where (0) | 2024.08.14 |
댓글