Kotlin의 sealed class, extensions 정리

패스트 캠퍼스 실무 프로젝트로 배우는 Kotlin & Spring 강의 정리

Sealed class

  • 하나의 상위 클래스나 인터페이스에서 하위 클래스의 정의를 제한할 수 있는 방법
  • sealed 클래스는 같은 패키지나 모듈에서만 하위 클래스를 정의할 수 있음
  • 아래의 코드에서는 Developer 클래스를 상속하는 클래스는 두 개 뿐이지만, when 식에서 else 구문이 없으면 컴파일 에러 발생
abstract class Developer {
    abstract val name: String
    abstract fun code(language: String)
}

data class BackendDeveloper(override val name: String) : Developer() {
    override fun code(language: String) {
        println("저는 백엔드 개발자입니다. $language 를 사용합니다.")
    }
}

data class FrontendDeveloper(override val name: String) : Developer() {
    override fun code(language: String) {
        println("저는 프론트엔드 개발자입니다. $language 를 사용합니다.")
    }
}

object DeveloperPool {
    val pool = mutableMapOf<String, Developer>()

    fun add(developer: Developer) = when(developer) {
        is BackendDeveloper -> pool[developer.name] = developer
        is FrontendDeveloper -> pool[developer.name] = developer
        else -> println("지원하지 않는 개발자입니다.")
    }

    fun get(name: String) = pool[name]
}
  • sealed 키워드를 이용하면 어떤 하위 클래스가 있는지 컴파일러가 판단할 수 있음
  • sealed 클래스를 상속한 하위 클래스가 있는데 when 식에서 정의되지 않으면 컴파일 에러 발생
sealed class Developer {
    abstract val name: String
    abstract fun code(language: String)
}

data class BackendDeveloper(override val name: String) : Developer() {
    override fun code(language: String) {
        println("저는 백엔드 개발자입니다. $language 를 사용합니다.")
    }
}

data class FrontendDeveloper(override val name: String) : Developer() {
    override fun code(language: String) {
        println("저는 프론트엔드 개발자입니다. $language 를 사용합니다.")
    }
}

object DeveloperPool {
    val pool = mutableMapOf<String, Developer>()

    fun add(developer: Developer) = when(developer) {
        is BackendDeveloper -> pool[developer.name] = developer
        is FrontendDeveloper -> pool[developer.name] = developer
    }

    fun get(name: String) = pool[name]
}

Extensions

  • 상속이나 데코레이터 패턴 등의 디자인 패턴을 사용하지 않고도 클래스를 확장하게 하는 기능
  • fun {클래스}.{생성할 함수 이름}
fun String.first(): Char {
  return this[0]
}
  • 수신자 객체(Receiver Object)
    • 확장 함수의 this 키워드는 수신자 객체와 일치
  • 확장하는 클래스에 동일한 이름의 메서드가 존재할 경우, 클래스의 멤버 함수가 우선시 됨
    • 확장하는 클래스에 동일한 시그니처가 있는지 확인해야 함
  • 수신자 객체가 nullable한 경우 확장 함수에서 null에 대한 처리를 하면 null 연산자 없이 사용 가능
class Example

fun Example?.printNullOrNotNull() {
  if (this == null) println("Null")
  else println("NotNull")
}

fun main() {
  var example: Example? = null
  example.printNullOrNotNull()	// null 연산자 없이 사용, Null
  
  example = Example()
  example.printNullOrNotNull()	// NotNull
}