아이템 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: 코딩 컨벤션을 지켜라
'Books > Effective Kotlin' 카테고리의 다른 글
[Effective Kotlin] 6장 클래스 설계 (0) | 2022.07.25 |
---|---|
[Effective Kotlin] 5장 객체 생성 (0) | 2022.07.18 |
[Effective Kotlin] 4장 추상화 설계 (0) | 2022.07.17 |
[Effective Kotlin] 3장 재사용성 (0) | 2022.07.17 |
[Effective Kotlin] 1장 안정성 (0) | 2022.06.21 |