흰 스타렉스에서 내가 내리지

Enum Class, Data Class, Inline Class 본문

Kotlin

Enum Class, Data Class, Inline Class

주씨. 2023. 9. 8. 20:19
728x90

# enum class

enum class WeekDay{ MON, TUE, WED, THU, FRI, SAT, SUN }

fun WeekDay.isWorkDay() = this == WeekDay.SAT || this == WeekDay.SUN

fun main(){
    println(WeekDay.MON.isWorkDay())    // false
    println(WeekDay.SAT.isWorkDay())    // true
}

 

# when

when을 사용하면 이넘 변수를 각각의 값과 비교할 수 있다. 

when 식에서 모든 이넘 상수를 다룬 경우에는 else 가지를 생략해도 된다. 

enum class Direction{
    NORTH, SOUTH, WEST, EAST
}

fun rotateClockWise(direction: Direction) = when (direction){
    Direction.NORTH -> Direction.EAST
    Direction.EAST -> Direction.SOUTH
    Direction.SOUTH -> Direction.WEST
    Direction.WEST -> Direction.NORTH
}

 

 

 

# 커스텀 멤버가 있는 이넘 정의하기

이넘에도 확장 함수나 프로퍼티를 붙일 수 있다. 

enum class WeekDay{
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;

    val lowerCaseName get() = name.lowercase()
    fun isWorkDay() = this == SATURDAY || this == SUNDAY
}

fun main(){
    println(WeekDay.MONDAY.isWorkDay())         // false
    println(WeekDay.WEDNESDAY.lowerCaseName)    // wednesday
}

 

 

이넘 클래스에 생성자가 있으면 각 이넘 상수의 정의 뒤에도 적절한 생성자 호출을 추가해야 한다.

enum class RainbowColor(val isCold: Boolean){
    RED(false), ORANGE(false), YELLOW(false),
    GREEN(true), BLUE(true), INDIGO(true), VIOLET(true);

    val isWarm get() = !isCold
}

fun main(){
    println(RainbowColor.BLUE.isCold)   // true
    println(RainbowColor.RED.isWarm)    // true
}

 

모든 이넘 값에는 ordinal과 name 이라는 한 쌍의 프로퍼티가 들어있다. 

ordinal은 이넘 클래스 안에 정의된 이넘 값의 순서에 따른 인덱스이고, name은 이넘 값의 이름이다. 

enum class Direction{
    NORTH, SOUTH, WEST, EAST
}

fun main(){
    println(Direction.WEST.name)    // WEST
    println(Direction.WEST.ordinal) // 2
}

 

각 이넘 클래스는 마치 동반 객체의 멤버처럼 호출할 수 있는 암시적인 메서드들을 제공한다.

valueOf() 메서드는 이넘 값의 이름을 문자열로 넘기면 그에 해당하는 이넘 값을 돌려주거나 이름이 잘못된 경우 예외를 던진다. 

enum class Direction{
    NORTH, SOUTH, WEST, EAST
}

fun main(){
    println(Direction.valueOf("NORTH"))     // NORTH
    println(Direction.valueOf("NORTH_EAST"))    // java.lang.IllegalArgumentException: No enum constant Direction.NORTH_EAST
}

 

 

코틀린1.1 부터는 values()나 valueOf() 대신에 제네릭 최상위 메서드인 enumValues()와 enumValueOf()를 사용할 수도 있다.

enum class WeekDay{
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
}


fun main(){
    val weekDays = enumValues<WeekDay>()
    println(weekDays[2])        // WEDNESDAY

    println(enumValueOf<WeekDay>("THURSDAY"))   // THURSDAY
}

 

 


#데이터 클래스

코틀린은 데이터를 저장하기 위한 목적으로 주로 쓰이는 클래스를 선언하는 유용한 기능을 제공한다. 

이런 기능을 데이터 클래스(data class)라고 부르며, 이 기능을 사용하면 컴파일러가 동등성을 비교하거나 String으로 변환하는 등의 기본 연산에 대한 구현을 자동으로 생성해준다. 

그리고 구조 분해 선언 (destructuring declaration)을 활용할 수도 있다. 

구조 분해를 사용하면 클래스의 프로퍼티를 간단한 한 가지 언어 구성 요소를 사용해 여러 변수에 나눠 넣을 수 있다. 

 

데이터 클래스라고 불리는 특정 유형의 클래스에 대해서는 코틀린이 클래스의 프로퍼티 목록을 기반으로 이런 메서드를 자동으로 생성해준다.

data class Person(
    val firstName: String,
    val familyName: String,
    val age: Int)

fun main(){
    val person1 = Person("John", "Doe", 25)
    val person2 = Person("John", "Doe", 25)
    val person3 = person1

    println(person1 == person2) // true
    println(person1 == person3) // true
}

data class는, 컴파일러가 주생성자에 정의된 프로퍼티의 값을 서로 비교하는 동등성 비교 연산을 자동으로 생성해주기 때문에 두 비교가 모두 true를 반환한다.

 

 

데이터 클래스는 equals()나 hashCode() 외에 toString() 메서드의 구현도 생성해준다. 

data class Person(
    val firstName: String,
    val familyName: String,
    val age: Int)

fun main(){
    val person1 = Person("John", "Doe", 25)

    println(person1) // Person@23fc625e -> Person(firstName=John, familyName=Doe, age=25)
}

 

 

모든 데이터 클래스는 암시적으로 copy() 함수를 제공한다. 

data class Person(
    val firstName: String,
    val familyName: String,
    val age: Int)

fun Person.show() = println("$firstName $familyName: $age")

fun main(){
    val person = Person("John", "Doe", 25)

    person.show()                                       // John Doe: 25
    person.copy().show()                                // John Doe: 25
    person.copy(familyName = "Smith").show()            // John Smith: 25
    person.copy(age = 30, firstName = "Jane").show()    // John Doe: 30

}

 

# 구조 분해 선언

데이터 클래스 인스턴스로부터 각각의 프로퍼티에 대응하는 지역 변수를 정의하는 간결한 구문이다. 

data class Person(
    val firstName: String,
    val familyName: String,
    val age: Int)

fun main(){
    val person: Person = Person("Ben", "Shelton", 20)
    val (a, b, c) = person
    println("$a $b $c")     // Ben Shelton 20
}

 

 

 

# 인라인 클래스 (값 클래스)

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

JVM 백엔드를 사용하는 경우에는 @JvmInline을 value class 앞에 반드시 붙여줘야 한다.

 

@JvmInline
value class Dollar(val amount: Int)

@JvmInline
value class Euro(val amount: Int)

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

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

런타임에는 호출 지점을 함수 본문으로 인라인하는 인라인 함수처럼 인라인 클래스 객체를 사용하는 위치 대신 인라인 클래스에 들어있는 값이 들어간다

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

@JvmInline
value 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)        // true
}

Dollar(15)와 같은 생성자 호출은 단순히 15라는 값을 언급하는 경우와 같아야 하므로, 인라인 클래스는 런타임에 어떠한 커스텀 코드도 실행할 수 없다. 

'Kotlin' 카테고리의 다른 글

마이크로서비스 구축  (0) 2023.09.09
Kotlin 컬렉션  (0) 2023.09.08
backing field  (1) 2023.09.08
Kotlin 기초  (4) 2023.09.05