들어가기 앞서
-
코틀린의 핵심 철학은 '실용주의(pragmatism)'
-
실용주의는 다음과 같은 비지니스 요구 사항을 충족한다는 것을 의미
-
생선성(productivity)
-
확장성(scalability)
-
유지보수성(maintainability)
-
신뢰성(reliability)
-
효율성(efficiency)
-
-
이 책의 목표
코틀린을 활용해서 더 안전하고, 더 가독성이 좋고, 더 확장성이 있으며, 더 효율적인 코드를 작성하는 방법 설명
좋은 코드
안정성
1. 가변성을 제한하라
-
코틀린의 프로퍼티는 상태(state)를 가질 수 있음.
read-write property(
var
) 혹은 mutable object -
상태를 가지는 것
-
프로그램 이해하기 어렵고 디버그 어려워짐
-
코드 실행을 추론하기 어려워짐
-
멀티스레드일 때 적절한 동기화 필요
-
테스트 어려워짐
-
변경을 알려야함
(정렬된 리스트일 경우 엘리먼트 변경시 매번 재정렬 필요)
-
-
가변성은 생각보다 단점이 많아 이를 제한하는 언어도 있음 → 순수 함수형 언어(Haskell, …)
-
일반적으로
var
보다val
을 많이 사용함 -
코틀린은 가변성을 제한할 수 있게 설계되어 있음
읽기 전용 프로퍼티(val
)
-
val
로 선언된 프로퍼티는 마치 값(value)처럼 동작 -
읽기 전용 프로퍼티가 완전히 변경 불가능한 것은 아니라는 것을 주의
val
은 읽기 전용 프로퍼티지만, 변경할 수 없음(불변, immutable)을 의미하는 것이 아니라는 것을 기억해야 함 -
읽기 전용 프로퍼티가 mutable 객체를 담고 있다면, 내부적으로 변경 가능
'프로퍼티를 읽을 수만 있다는 것(읽기 전용) vs. 값이 변할 수 없는 것(가변성)' 구분할 줄 알아야 함
-
읽기 전용 프로퍼티는 다른 프로퍼티를 활용하는 사용자 정의 게터로도 정의 가능
var id = "1" var name = "jun" val key get() = "$id:$name" (1) fun main() { println(key) // 1:jun id = 2 println(key) // 2:jun (2) }
-
사용자 정의 게터
-
val
임에도 불구하고 값이 변경됨
-
-
코틀린의 프로퍼티는 기본적으로 캡슐화 되어 있고, 사용자 정의 접근자(getter, setter) 가질 수 있음 = 유연함 제공
(아이템 16 참고)
-
val
을var
로 오버라이드 가능interface Element { val active: Boolean } class ActualElement: Element { override var active: Boolean = false } fun main() { with(ActualElement()) { println(active) // false active = true println(active) // true } }
가변 컬렉션과 읽기 전용 컬렉션 구분
-
읽기 전용 인터페이스:
Iterable
,Collection
,Set
,List
-
읽고 쓸 수 있는 인터페이스:
MutableIterable
,MutableCollection
,MutableSet
,MutableList
-
읽기 전용 컬렉션을 mutable 컬렉션으로 다운캐스팅하면 안됨
(해당 인터페이스의 계약을 위반하고, 추상화를 무시하는 행위. 안전하지 못하고, 예측하지 못한 결과를 초래함)
val list = listOf(1, 2, 3) // 하지 말아야 할 행위. 다운캐스팅 if (list is MutableList) { list.add(4) }
-
복제(copy)를 통해 새로운 mutable 컬렉션을 만드는
list.toMutableList
를 활용해야 함
데이터 클래스의 copy
-
data
한정자는copy
라는 이름의 메서드를 만들어 줌 -
String
이나Int
처음 내부적인 상태를 변경하지 않는 immutable 객체를 많이 사용하는 데에는 이유가 있음-
변경되지 않으므로(상태가 유지되므로) 코드 이해하기 쉬움
-
공유시 충돌 X, 안전하게 병렬 처리
-
쉽게 캐싱할 수 있음
-
방어적 복사본(defensive copy)을 만들 필요가 없음. 또한 깊은 복사하지 않아도 됨
-
실행을 쉽게 예측할 수 있음
-
맵, 셋의 키로도 사용 가능
-
2. 변수의 스코프를 최소화하라
-
상태를 정의할 떄 변수나 프로퍼티의 스코프를 최소화하라
-
프로그램을 추적하고 관리하기 쉬워짐
-
다른 개발자에 의해 잘못 사용되는 것을 막음
-
코틀린의 if, when, try-catch, Elvis 표현식 활용 가능
-
구조분해 선언(destructuring declaration) 활용 가능
-
람다에서 변수를 캡쳐하는 것 주의
3. 최대한 플랫폼 타입을 사용하지 말라
-
플랫폼 타입(platform type): 다른 프로그래밍 언어에서 전달되어서 nullable인지 아닌지 알 수 없는 타입
-
String!
과 같이 타입 뒤에!
기호를 붙혀서 표기
-
-
자바와 코틀린을 함께 사용할 때, 자바 코드를 직접 조작할 수 있다면 가능한
@Nullable
,@NotNull
어노테이션을 붙여서 사용-
JetBrains, JSR-305, ReactiveX, Lombok 등에 관련 어노테이션이 존재함
-
-
플랫폼 타입이 전파되는 일은 위험
-
항상 위험을 내포, 안전한 코드를 원한다면 이런 코드를 제거
-
public class JavaClass {
public String getValue() { return null; }
}
fun statedType() {
val value: String = JavaClass().value // NPE
println(value.length)
}
fun platformType() {
val value = JavaClass().value
println(value.length) // NPE
}
4. inferred 타입으로 리턴하지 말라
-
코틀린의 타입 추론(type inference)은 JVM 세계제어 가장 널리 알려진 코틀린의 특징
-
할당 때 inferred 타입은 정확하게 오른쪽에 있는 피 연산자에 맞게 설정됨. 절대 슈퍼클래스 or 인터페이스로 설정되지 않음
open class Animal class Zebra: Animal() fun main() { var animal = Zebra() // (1) animal = Animal() // 오류: Type mismatch }
-
타입을 선언하지 않고 추론을 통해 타입 지정
-
-
타입을 확실하게 지정해야 하는 경우 명시적으로 타입을 지정.
-
타입은 굉장히 중요한 정보이므로, 숨기지 않는 것이 좋음
5. 예외를 활용해 코드에 제한을 걸어라
-
다양한 방법으로 코드에 제한을 걸 수 있음
-
require
블록: 인자를 제한할 수 있음-
require
함수는 조건을 만족하지 못할 때 무조건IllegalArgumentException
을 throw 함
-
-
check
블록: 상태와 관련된 동작을 제한할 수 있음-
이런 경우에
check
함수를 하용함-
어떤 객체가 미리 초기화되어 있어야만 처리를 하게 하고 싶은 함수
-
사용자가 로그인 했을 때만 처리하게 하고 싶은 함수
-
객체를 사용할 수 있는 시점에 사용하고 싶은 함수
-
-
IllegalStateException
을 throw함 -
일반적으로
require
블록 뒤에 배치
-
-
assert
블록: 어떤 것이 true인지 확인할 수 있음.assert
블록은 테스트 모드에서만 작동. -
return
orthrow
와 함께 활용하는 Elvis 연산자
-
-
제한을 걸면 문서를 읽지 않은 개발자도 문제를 확인할 수 있음
-
코드가 어느 정도 자체적으로 검사됨
-
스마트 캐스트 기능 활용
6. 사용자 정의 오류보다는 표준 오류를 사용하라
-
require
,check
,assert
함수를 사용하면 대부분의 코틀린 오류를 처리할 수 있음 -
최대한 표준 라이브러리의 오류를 사용하는 것이 좋음
-
많은 개발자가 알고 있으므로 이를 재사용
-
-
나타내기 위한 적절한 오류가 없을 경우 사용자 정의 오류 사용
-
but, 설정보다는 관례
-
-
"규칙 60. 표준 예외를 사용하라" - 이펙티브 자바 2판
7. 결과 부족이 발생할 경우 null과 Failer를 사용하라
코드 설계
재사용성
8. knowledge를 반복하여 사용하지 말라
Appendix A: test
Glossary
- mud
-
wet, cold dirt
- rain
-
water falling from the sky