본문은 Effective Kotlin을 읽고 간단하게 정리한 글입니다. 필요에 따라 생략/수정된 부분이 있을 수 있으며, 내용이 추후 변경될 수 있습니다.
1. 스코프를 최소화하기 위한 규칙
상태를 정의할 땐 변수와 프로퍼티의 스코프를 최소화 하는 것이 좋고, 이를 위해선 다음과 같은 규칙들을 활용할 수 있다.
1) 프로퍼티보다는 지역 변수를 사용하라
2) 최대한 좁은 스코프를 갖게 변수를 사용하라
// 1. Bad
var user: User // for문 외부에서도 사용 가능함
for (i in users.indicies) {
user = users[i]
print("User at $i is $user")
}
// 2. Better
for (i in users.indices) {
val user = users[i] // 지역변수 사용
print("User at $i is $user")
}
// 3. Best
for ((i, user) in users.withIndex()) {
print("User at $i is $user") // 별도 변수 선언 X
}
3) 변수는 최대한 정의할 때 초기화하라
// Bad
val user: User
if (hasValue) {
user = getValue()
} else {
user = User()
}
// Better
val user: User = if (hasValue) {
getValue()
} else {
User()
}
- if / when / try-catch / Elvis 표현식 등을 활용하면 최대한 변수를 정의할 때 초기화 할 수 있다
// Bad
fun updateWeather(degrees: Int) {
val description: String
val color: String
if (degrees < 5) {
description = "cold"
color = "BLUE"
} else if (degrees < 23) {
description = "mild"
color = "YELLOW"
} else {
description = "hot"
color = "RED"
}
}
// Better
fun updateWeather(degrees: Int) {
val (description, color) = when {
degrees < 5 -> "color" to "BLUE"
degrees < 23 -> "mild" to "YELLOW"
else -> "hot" to "RED"
}
}
- 여러 값을 한 번에 초기화해야 하는 경우에는 구조분해 선언(destructing declaration)을 활용하는 것이 좋다
2. 왜 스코프를 좁게 만들어야 하는가?
다음과 같은 이유로 스코프를 좁게 만들어야 한다.
- 프로그램 추적 및 관리가 쉬워진다 (= 가독성이 높아진다)
- 변수의 스코프가 너무 넓으면 다른 사람에 의해 변수가 잘못 사용될 수 있다
- 캡처링에 대한 이슈가 있음
첫 번째나 두 번째 이유는 너무 당연하기도 하고 쉬운 내용이니 생략하고, 본문의 예제를 가지고 캡처링(=람다 캡처링, Lambda Capturing)에 대해 좀 더 살펴보자. 람다 캡처링은 람다 외부에서 정의된 변수를 참조하는 것을 의미한다.
코틀린의 일반적인 Collection은 step-by-step으로 연산이 수행되는 반면, 코틀린의 Sequence는 자바의 Stream과 유사하게 lazy하게 연산이 일어난다는 특징이 있다. (참고)

1) 올바른 예제
val primes: Sequence<Int> = sequence {
var numbers = generateSequence(2) { it + 1 }
while (true) {
val prime = numbers.first()
yield(prime)
numbers = numbers.drop(1)
.filter { it % prime != 0}
}
}
print(primes.take(10).toList()) // [2, 3, 5, 7. 11, 13, 17, 19, 23, 29]
위 코드는 다음과 같이 실행된다.
변수 prime은 매 루프마다 생성된므로 filter에 들어가는 값도 모두 다르다.
// 1단계)
numbers.first ()
-> prime = 2
// 2단계)
numbers.drop(1).filter { it % 2 != 0 }
.first()
- 2가 drop & 3, 5, 7 등이 filter 통과
-> prime = 3
// 3단계)
numbers.drop(1).filter { it % 2 != 0 }
.drop(1).filter { it % 3 != 0 }
.first()
- 2가 drop & 3, 5, 7 등이 filter 통과
- 3이 drop & 5, 7, 11 등이 filter 통과
-> prime = 5
// 4단계)
numbers.drop(1).filter { it % 2 != 0 }
.drop(1).filter { it % 3 != 0 }
.drop(1).filter { it % 5 != 0 }
.first()
- 2가 drop & 3, 5, 7 등이 filter 통과
- 3이 drop & 5, 7, 11 등이 filter 통과
- 5가 drop & 7, 11, 13 등이 filter 통과
-> prime = 7
...
2) 잘못된 예제
val primes: Sequence<Int> = sequence {
var numbers = generateSequence(2) { it + 1 }
var prime: Int
while (true) {
prime = numbers.first()
yield(prime)
numbers = numbers.drop(1)
.filter { it % prime != 0}
}
}
print(primes.take(10).toList()) // [2, 3, 5, 6, 7, 8, 9, 10, 11, 12]
위 코드는 다음과 같이 실행된다.
1번 예제와 다르게 prime이 캡처링되었다. 값이 담기는 게 아니라 외부 변수를 참조하므로 최신으로 갱신된 prime이 filter에 적용된다.
// 1단계)
numbers.first ()
-> prime = 2
// 2단계)
numbers.drop(1).filter { it % 2 != 0 }
.first()
- 2가 drop & 3, 5, 7 등이 filter 통과
-> prime = 3
// 3단계)
numbers.drop(1).filter { it % 3 != 0 }
.drop(1).filter { it % 3 != 0 }
.first()
- 2가 drop & 4, 5, 7 등이 filter 통과
- 4가 drop & 5, 7, 8 등이 filter 통과
-> prime = 5
// 4단계)
numbers.drop(1).filter { it % 5 != 0 }
.drop(1).filter { it % 5 != 0 }
.drop(1).filter { it % 5 != 0 }
.first()
- 2가 drop & 3, 4, 6 등이 filter 통과
- 3이 drop & 4, 6, 7 등이 filter 통과
- 4가 drop & 6, 8, 9 등이 filter 통과
-> prime = 6
...
+ 추가로 책의 설명 중 애매한 내용이 있다.
"prime이 2로 설정되어 있을 때 필터링된 4를 제외하면, drop만 동작하므로 그냥 연속된 숫자가 나와 버립니다."라고 적혀있다.
"캡처링으로 인해 필터링이 제대로 이뤄지지 않고 drop만 동작하는 상황이므로, prime이 3으로 설정되어 있을 때 드랍된 4를 제외한 연속적인 숫자가 나온다."가 좀 더 정확한 설명일 것이다.
3. 정리
- 스코프를 좁게 만들어서 활용하라
- var보다는 val을 쓰는 것이 좋다
- 람다에서 변수를 캡처하는 것을 기억하라
4. 참고
'책 > Effective Kotlin' 카테고리의 다른 글
[이펙티브 코틀린] 아이템7: 결과 부족이 발생할 경우 null과 Failure를 사용하라 (0) | 2023.07.04 |
---|---|
[이펙티브 코틀린] 아이템5: 예외를 활용해 코드에 제한을 걸어라 (0) | 2023.07.03 |
[이펙티브 코틀린] 아이템4: inferred 타입으로 리턴하지 말자 (0) | 2023.07.02 |
[이펙티브 코틀린] 아이템3: 최대한 플랫폼 타입을 사용하지 말라 (0) | 2023.07.01 |
[이펙티브 코틀린] 아이템1: 가변성을 제한하라 (0) | 2023.06.28 |
본문은 Effective Kotlin을 읽고 간단하게 정리한 글입니다. 필요에 따라 생략/수정된 부분이 있을 수 있으며, 내용이 추후 변경될 수 있습니다.
1. 스코프를 최소화하기 위한 규칙
상태를 정의할 땐 변수와 프로퍼티의 스코프를 최소화 하는 것이 좋고, 이를 위해선 다음과 같은 규칙들을 활용할 수 있다.
1) 프로퍼티보다는 지역 변수를 사용하라
2) 최대한 좁은 스코프를 갖게 변수를 사용하라
// 1. Bad
var user: User // for문 외부에서도 사용 가능함
for (i in users.indicies) {
user = users[i]
print("User at $i is $user")
}
// 2. Better
for (i in users.indices) {
val user = users[i] // 지역변수 사용
print("User at $i is $user")
}
// 3. Best
for ((i, user) in users.withIndex()) {
print("User at $i is $user") // 별도 변수 선언 X
}
3) 변수는 최대한 정의할 때 초기화하라
// Bad
val user: User
if (hasValue) {
user = getValue()
} else {
user = User()
}
// Better
val user: User = if (hasValue) {
getValue()
} else {
User()
}
- if / when / try-catch / Elvis 표현식 등을 활용하면 최대한 변수를 정의할 때 초기화 할 수 있다
// Bad
fun updateWeather(degrees: Int) {
val description: String
val color: String
if (degrees < 5) {
description = "cold"
color = "BLUE"
} else if (degrees < 23) {
description = "mild"
color = "YELLOW"
} else {
description = "hot"
color = "RED"
}
}
// Better
fun updateWeather(degrees: Int) {
val (description, color) = when {
degrees < 5 -> "color" to "BLUE"
degrees < 23 -> "mild" to "YELLOW"
else -> "hot" to "RED"
}
}
- 여러 값을 한 번에 초기화해야 하는 경우에는 구조분해 선언(destructing declaration)을 활용하는 것이 좋다
2. 왜 스코프를 좁게 만들어야 하는가?
다음과 같은 이유로 스코프를 좁게 만들어야 한다.
- 프로그램 추적 및 관리가 쉬워진다 (= 가독성이 높아진다)
- 변수의 스코프가 너무 넓으면 다른 사람에 의해 변수가 잘못 사용될 수 있다
- 캡처링에 대한 이슈가 있음
첫 번째나 두 번째 이유는 너무 당연하기도 하고 쉬운 내용이니 생략하고, 본문의 예제를 가지고 캡처링(=람다 캡처링, Lambda Capturing)에 대해 좀 더 살펴보자. 람다 캡처링은 람다 외부에서 정의된 변수를 참조하는 것을 의미한다.
코틀린의 일반적인 Collection은 step-by-step으로 연산이 수행되는 반면, 코틀린의 Sequence는 자바의 Stream과 유사하게 lazy하게 연산이 일어난다는 특징이 있다. (참고)

1) 올바른 예제
val primes: Sequence<Int> = sequence {
var numbers = generateSequence(2) { it + 1 }
while (true) {
val prime = numbers.first()
yield(prime)
numbers = numbers.drop(1)
.filter { it % prime != 0}
}
}
print(primes.take(10).toList()) // [2, 3, 5, 7. 11, 13, 17, 19, 23, 29]
위 코드는 다음과 같이 실행된다.
변수 prime은 매 루프마다 생성된므로 filter에 들어가는 값도 모두 다르다.
// 1단계)
numbers.first ()
-> prime = 2
// 2단계)
numbers.drop(1).filter { it % 2 != 0 }
.first()
- 2가 drop & 3, 5, 7 등이 filter 통과
-> prime = 3
// 3단계)
numbers.drop(1).filter { it % 2 != 0 }
.drop(1).filter { it % 3 != 0 }
.first()
- 2가 drop & 3, 5, 7 등이 filter 통과
- 3이 drop & 5, 7, 11 등이 filter 통과
-> prime = 5
// 4단계)
numbers.drop(1).filter { it % 2 != 0 }
.drop(1).filter { it % 3 != 0 }
.drop(1).filter { it % 5 != 0 }
.first()
- 2가 drop & 3, 5, 7 등이 filter 통과
- 3이 drop & 5, 7, 11 등이 filter 통과
- 5가 drop & 7, 11, 13 등이 filter 통과
-> prime = 7
...
2) 잘못된 예제
val primes: Sequence<Int> = sequence {
var numbers = generateSequence(2) { it + 1 }
var prime: Int
while (true) {
prime = numbers.first()
yield(prime)
numbers = numbers.drop(1)
.filter { it % prime != 0}
}
}
print(primes.take(10).toList()) // [2, 3, 5, 6, 7, 8, 9, 10, 11, 12]
위 코드는 다음과 같이 실행된다.
1번 예제와 다르게 prime이 캡처링되었다. 값이 담기는 게 아니라 외부 변수를 참조하므로 최신으로 갱신된 prime이 filter에 적용된다.
// 1단계)
numbers.first ()
-> prime = 2
// 2단계)
numbers.drop(1).filter { it % 2 != 0 }
.first()
- 2가 drop & 3, 5, 7 등이 filter 통과
-> prime = 3
// 3단계)
numbers.drop(1).filter { it % 3 != 0 }
.drop(1).filter { it % 3 != 0 }
.first()
- 2가 drop & 4, 5, 7 등이 filter 통과
- 4가 drop & 5, 7, 8 등이 filter 통과
-> prime = 5
// 4단계)
numbers.drop(1).filter { it % 5 != 0 }
.drop(1).filter { it % 5 != 0 }
.drop(1).filter { it % 5 != 0 }
.first()
- 2가 drop & 3, 4, 6 등이 filter 통과
- 3이 drop & 4, 6, 7 등이 filter 통과
- 4가 drop & 6, 8, 9 등이 filter 통과
-> prime = 6
...
+ 추가로 책의 설명 중 애매한 내용이 있다.
"prime이 2로 설정되어 있을 때 필터링된 4를 제외하면, drop만 동작하므로 그냥 연속된 숫자가 나와 버립니다."라고 적혀있다.
"캡처링으로 인해 필터링이 제대로 이뤄지지 않고 drop만 동작하는 상황이므로, prime이 3으로 설정되어 있을 때 드랍된 4를 제외한 연속적인 숫자가 나온다."가 좀 더 정확한 설명일 것이다.
3. 정리
- 스코프를 좁게 만들어서 활용하라
- var보다는 val을 쓰는 것이 좋다
- 람다에서 변수를 캡처하는 것을 기억하라
4. 참고
'책 > Effective Kotlin' 카테고리의 다른 글
[이펙티브 코틀린] 아이템7: 결과 부족이 발생할 경우 null과 Failure를 사용하라 (0) | 2023.07.04 |
---|---|
[이펙티브 코틀린] 아이템5: 예외를 활용해 코드에 제한을 걸어라 (0) | 2023.07.03 |
[이펙티브 코틀린] 아이템4: inferred 타입으로 리턴하지 말자 (0) | 2023.07.02 |
[이펙티브 코틀린] 아이템3: 최대한 플랫폼 타입을 사용하지 말라 (0) | 2023.07.01 |
[이펙티브 코틀린] 아이템1: 가변성을 제한하라 (0) | 2023.06.28 |