반응형

아이템 11: 가독성을 목표로 설계하라

 

아이템 12: 연산자 오버로드를 할 때는 의미를 맞게 사용하라

 

아이템 13: Unit?을 리턴하지 말라

 

아이템 14: 변수 타입이 명확하지 않은 경우 확실하게 지정하라

 

아이템 15: 리시버를 명시적으로 참조하라

코틀린에서는 리시버라는 개념이 존재한다. 

리시버는 예시로 다음과 같은 경우에서 사용된다.

  • 클래스에서 내부에서의 클래스 참조
  • 확장함수
  • with, apply, run 등의 scope 함수

다음과 같은 코드가 있다고 생각해보자.

이는 사실 잘못된 코드이긴 하지만, makeChild의 name이 parent가 될지, child가 될지 애매하다. 리시버를 암시적으로 사용(this를 생략)했기 때문이다.

class Node(val name: String){
    fun makeChild(childName: String) = 
        create("name.$childName")
            .apply{ print("Create $name") }
    
    fun create(name:String): Node? = Node(name)
}

fun main(){
    val node = Node("parent")
    node.makeChild("child")
}

위 예시처럼 리시버를 중첩으로 사용하게 될 경우 명시적으로 선언해주지 않으면 참조하는 값이 어떤 값을 나타내는지가 애매해진다.

그래서 아래처럼 명시적으로 리시버가 어떤 값을 참조했는지 표현해주는게 좋다.

class Node(val name: String){
    fun makeChild(childName: String) =
        create("name.$childName")
            .apply{ print("Create ${this?.name}") }

    fun create(name:String): Node? = Node(name)
}

fun main(){
    val node = Node("parent")
    node.makeChild("child")
}

 

코틀린에서는 레이블이라는 것도 있다.

위 예시처럼 중첩 리시버를(사용 안하는게 좋지만) 불가피하게 사용해야 할 때, 스코프 외부의 리시버를 사용해야 할 상황이 있을 수 있다. 이러한 상황에서는 this@where 을 사용하면 된다. 여기서 where은 어떤 리시버를 참조할지에 대한 위치이다.

이런식으로!

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

    fun create(name:String): Node? = Node(name)
}

fun main(){
    val node = Node("parent")
    node.makeChild("child")
}

 

정리

  • 리시버는 명시적으로 사용하는 것을 지향하자(this 사용)
  • 스코프 외부의 리시버를 사용할 때는 레이블을 사용하자

아이템 16: 프로퍼티는 동작이 아니라 상태를 나타내야 한다

코틀린의 프로퍼티(변수)는 기본적으로 getter/setter를 제공한다.

이런식으로!

var은 Mutable이기 때문에 getter/setter, val은 Immutable이기 때문에 getter를 제공한다.

var name: String? = null
    get() = field?.toUpperCase()
    set(value){
        if(!value.isNullOrBlank()){
            field = value
        }
    }
val fullName: String
    get() = "$name $surname"

위에서 field라는 식별자를 볼 수 있는데, 이는 프로퍼티에 대한 레퍼런스이다.

이를 활용하면 단순히 데이터의 저장, 참조를 위해서 함수를 사용하지 않고 프로퍼티만으로 해결할 수 있다.

effective kotlin 책에서는 다음과 같은 경우를 지양하라고 말한다.

(1) 프로퍼티의 getter/setter에 로직을 담는 경우

// 이렇게 하지 마세요!
val Tree<Int>.sum: Int
    get() = when(this){
        is Leaf -> value
        is Node -> left.sum + right.sum
    }

 

(2) 클래스의 프로퍼티가 단순히 데이터 저장용일 때, 의미없이 getter/setter를 만드는 경우

// 이렇게 하지 마세요!
class UserIncorrect {
    private var name: String = ""

    fun getName() = name
    fun setName(name: String){
        this.name = name
    }
}

 

정리

  • 프로퍼티의 이점을 활용해 코드 양을 줄이자!(java처럼 getter/setter 만들지 말고, kotlin스럽게 코딩하기!)

 

아이템 17: 이름있는 아규먼트를 사용하라

 

아이템 18: 코딩 컨벤션을 지켜라

반응형

+ Recent posts