kyucumber
전체 글 보기

이펙티브 코틀린 아이템 15. 리시버를 명시적으로 참조하라

무언가를 자세히 설명하기 위해 명시적으로 더 긴 코드를 사용할 때가 있다.

  • 클래스의 메소드임을 나타내기 위해 this를 사용하는 경우
class Test { private var num: Int = 0 fun test() { this.num += 1 }
  • 확장 리시버를 명시적으로 참조하게 하는 경우
fun <T: comparable<T>> List<T>.quickSort(): List<T> { // ... val pivot = this.first() // this를 붙인 부분 // ... }

여러 개의 리시버

스코프 내부에 둘 이상의 리시버가 있는 경우 리시버를 명시적으로 나타내는게 좋다.

  • AS-IS
class Node(val name: String) { fun makeChild(childName: String) = create("$name.$childName").apply { println("Created $name") } private fun create(name: String): Node? = Node(name) }

위의 예는 name이 Node의 name을 참조하는 것이 아니라 생성자로 전달된 name을 참조하게 된다.

  • TO-BE
class Node(val name: String) { fun makeChild(childName: String) = create("$name.$childName").apply { println("Created ${this?.name}") } private fun create(name: String): Node? = Node(name) }

수정된 코드에서 문제는 해결되었지만 apply가 아닌 also를 사용했다면 이런 문제 자체가 일어나지 않는다.

일반적으로 also, let을 사용하는 것이 nullable 값을 처리할 때 훨씬 좋은 선택지이다.

create("$name.$childName").also { println("Created $it?.name") }

리시버가 명확하지 않으면 아래와 같이 명시적으로 리서버를 적어서 명확하게 하자.

class Node(val name: String) { fun makeChild(childName: String) = create("$name.$childName").apply { println("Created ${this?.name} ${this@Node.name}") } private fun create(name: String): Node? = Node(name) }

DSL 마커

DSL을 사용할 때는 리시버를 가진 요소가 중첩되더라도 명시적으로 리시버를 붙이지 않는다.

table { tr { td { +"Column 1" } } }

기본적으로 모든 스코프에서 외부 스코프 리시버의 메소드를 사용할 수 있지만 아래와 같은 경우 코드에 문제가 발생한다.

table { tr { td { +"Column 1" } tr { td { +"Value 1" } } } }

이러한 잘못된 사용을 막으려면 암묵적으로 외부 리시버를 사용하는것을 막는 DslMarker라는 메타 어노테이션을 사용해야 한다.

@DslMarker annotation class HtmlDsl @DslMarker annotation class TableDsl

이렇게 하면 외부 리시버 사용이 금지되며, 원한다면 명시적으로 지정해 사용할 수 있다.

table { tr { td { +"Column 1" } tr { // 컴파일 오류 td { +"Value 1" } } } } table { tr { td { +"Column 1" } this@table.tr { td { +"Value 1" } } } }

정리

  • 짧게 적을 수 있다는 이유만으로 리시버를 제거하지 말자.
  • 여러개의 리시버가 있는 상황 등에는 리시버를 명시적으로 적어주는 것이 좋다.
  • DSL에서 외부 스코프의 리시버를 적게 강제하고 싶다면 DslMarker 메타 어노테이션을 사용하자

Reference

  • 이펙티브 코틀린 - 프로그래밍 인사이트, 마르친 모스칼라 지음, 윤인성 옮김

개인적인 기록을 위해 작성된 글이라 잘못된 내용이 있을 수 있습니다.

오류가 있다면 댓글을 남겨주세요.