중복되는 내용이지만 Kotlin in Action 1,2,3,4장 정리를 모아놓았습니다.

Kotlin

1장

  • 코틀린의 빌더 패턴이란, 디자인 패턴 빌더 패턴과 같다. (p.44)
JuiceBuilder()
        .water(100)
        .ice(20)
        .orange(5)
        .banana(10)
        .build()

이처럼 Builder를 만들어 사용하는데, 파라미터가 명시적이고 필수 파라미터를 확인할 수 있다. 외에도 여러 이점이 있어 Lombok에서도 지원하는 패턴인데 코틀린의 경우,

Juice(
    water = 100,
    ice = 20,
    oranger = 5,
    banana = 10
)

생성자에 변수 명을 명시할 수 있고, 매개변수별로 기본값을 가질 수 있다. 때문에 이 자체로 빌더 패턴이 구현되어 있어 이를 용이하게 사용하면 좋다.

  • 1장은 코틀린의 주요 특성인 다양한 플랫폼, 정적 타입 언어, 함수형 + 객체 지향, 무료 오픈소스 대해 말한다.
  • 코틀린의 철학
    • 실용성
    • 간결성
    • 안전성
    • 상호운용성

2장

  • 대입문은 자바에서 식, 코틀린에서는 문이다.
    • 코틀린에서는 a = b = c와 같이 사용할 수 없다.
  • 모든 변수를 val 선언, 변경이 불가피할때만 var 이용하자.
  • 자바의 문자열 접합 연산은 내부적으로 StringBuilder를 사용해 효율적이다.
    • 코틀린의 문자열 템플릿 또한 마찬가지로 효율적.
  • 프로퍼티를 기본으로 지원하여, Class의 필드에 대해 게터 세터가 생성된다.
  • 변수 타입은 is로 검사하고, 이후엔 스마트 캐스트를 통해 캐스팅하지 않아도 검사된 타입으로 취급한다.
if (sth is Int) { val newSth = sth as Int } // 불필요
// 캐스팅하지 않아도 if 안에선 sth은 Int로 스마트 캐스팅 되어있다.
  • Exception 또한 식이다. 자바와 사용은 같으나 throws 절이 없다.
    • 자바는 checked Exception 에 대해 명시적 처리를 하지만 코틀린은 checked, unchecked를 구분하지 않는다.

3장 함수 정의와 호출

  • 프로퍼티도 최상위 수준에 놓을 수 있다. (함수도)
package blog

val aString: String
var aInt: Int = 0
const val THE_INT = 1

fun test() = println("HI test")
  • const를 붙이면 pubilc static final 이 된다.
  • fun String.lastChar(): Char 처럼 확장할 클래스의 이름(수신 객체 타입)을 명시하고 확장 함수를 정의할 수 있다.
  • 확장함수가 캡슐화를 깨지는 않는다. 내부적으로 수신 객체를 첫 번째 인자로 받는 정적 메서드이다.
    • 당연히 확장 함수 내부에서 private, protected 멤버는 접근이 불가하다.
    • 확장 함수는 단지 정적 메서드 호출에 대한 문법적인 편의(syntatic sugar)
  • Java ... == Kotlin vararg (가변 길이 인자)
    • *aList는 배열을 명시적으로 풀어준다.
  • 중위 호출은 infix 변경자를 함수 앞에 붙여주어 구현한다.
    • ex) infix fun Any.to(other: Any) = Pair(this, other)

4장 클래스, 객체, 인터페이스

  • interface는 구현된 메서드를 가진다.(default method 처럼)
    • default 구현이 둘 이상의 인터페이스가 가지면, override 필수
  • Kotlin의 class, method는 기본이 final
    • 상속을 허용하려면 open (override 메서드는 open이다.)
    • 이로 인해 스마트 캐스트가 가능하다는 이점이 생긴다.
      • 프로퍼티가 val + 커스텀 접근자 x 일 때만 스마트 캐스트
      • 상속 후 프로퍼티의 커스텀 접근자를 정의하면 스마트 캐스트 요구 사항이 깨질 수 있다.
  • package-private은 없고, 모듈 내부에서만 접근 가능한 internal
internal open class TalkativeButton: Focusable {
    private fun yell() = println("HEY!")
    protected fun whisper() = println("whisper") 
}

fun TalkativeButton.giveSpeech() { // ERROR: internal class이므로 노출할 수 없다. 
    yell() // ERROR: private method
  
    whisper() // ERROR: protected method 같은 패키지에서 접근이 불가하다.
}
  • kotlin nested class는 변경자가 없으면 java 내부 static class와 같다.
    • 바깥 클래스에 대한 참조를 포함하게 하려면 inner 변경자를 사용한다.
  • sealed 클래스 상속을 제한한다.
    • 하위 클래스는 모두 상위 클래스의 nested class 로 존재해야한다.
    • when에서 else문이 강제되지 않아, 예외로 인한 버그를 예방한다.

sealed 접근자의 제한과 코틀린 1.5에서 개선된 점을 알아보쟈 sealed 접근자

  • 주 생성자, 부 생성자, 초기화 블록 등이 있다.
    • DI 프레임워크 등에서 parameter 없는 생성자가 필요한 경우가 있는데, 코틀린은 default 값을 통해 해결한다. (안전성과 가독성에 괜찮은걸까?)
    • 인터페이스는 프로퍼티를 가질 수 있고, 이는 하위 클래스가 확장 시 구현해야한다.
  • 프로퍼티는 값을 저장하는 프로퍼티, 커스텀 접근자에서 매번 계산하는 프로퍼티 두 가지가 있다.
    • 쉽게 말해 게터를 정의한 경우와, 값이 대입된 프로퍼티 두 가지이다.
      • setter에서 field로 필드 값을 가져올 수 있다.
      • private get, private set으로 접근자의 가시성 변경 가능하다.
  • by 키워드로 위임을 할 수 있다.
    • 코틀린은 상속에서 발생하는 문제를 위해 기본이 final이다.

    상속 불가인 클래스에 대해 데코레이터 패턴을 이용해 하위 타입처럼 생성할 수 있다.
    • 데코레이터 패턴의 구현의 복잡성을 지원해주는 것이 바로 by
class DelegatingCollection<T>: Collection<T> {
    private val innerList = arrayListOf<T>()
    override val size: Int = innerList.size
    override fun isEmpty(): Boolean = innerList.isEmpty()
      ...
}

class DelegatingCollection<T>(
    innerList: Collection<T> = ArrayList<T>()
) : Collection<T> by innerList {}
  • 위의 과정이 아래와 같이 단순해진다.
  • object 선언은 싱글턴 패턴을 지원한다.
  • 코틀린은 static 이 없지만, 패키지 수준의 최상위 함수 + object가 있다.
  • Companion Object 내부에 private 생성자, 정적 멤버를 생성하기 좋다
    • Companion Object는 바깥 클래스의 private 멤버에 접근이 가능하기 때문이다.
    • 여기에 팩토리 패턴을 구현하자.
    • 자바에서 사용하기 위해 static으로 만들고 싶다면 @JvmStatic