1장 코틀린 설치와 실행

 

apply plugin: 'kotlin-android'

- 안드로이드용 코틀린 플러그인 적용

 

apply plugin: 'kotlin-android-extensions'

- 안드로이드 코틀린 익스텐션 적용

 

implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"

- 코틀린 표준 라이브러리 의존성.

- JDK7 혹은 JDK8 용 확장 함수 사용 및 바이트코드 생성 가능

 

kotlin-reflect

- 리플렉션을 위함

 

kotlin-test / kotlin-test-junit

- 코틀린 테스트를 위함

 

 

2장 코틀린 기초

 

https://mg-laboratory.tistory.com/256

 

  - 단언 (!!) 연산자가 하나라도 있다는 이는 Code Smell 이다. 가능하면 사용하지 않도록 노력하자

  - val middleNameLength = p.middle?.length ?: 0

 

@JvmOverloads

  -  코틀린에서는 모든 인자를 전달하지 않더라도 오버로딩이 동작하지만, 이를 Java 에서호출하기 위해서는 어노테이션 추가가 필요하다.

 

@JvmOverlads

fun addProduct(name: String, price: Double = 0.0, desc: String? = null)

  - Java 의 super 가 호출되는 것이 아니라 각각의 생성자가 모두 추가된다

 

 

3장 코틀린 객체 지향 프로그래밍

 

- 컴파일 타임 상수(const val)는 반드시 object 나 companion object 선언의 최상위 속성 또는 멤버여야 한다

- 문자열 또는 기본 타입의 래퍼 클래스이며 getter 를 가질 수 없다

 

Backing Field

  - 프로퍼티에 대한 접근을 감시하고, 제어하는 숨겨진 필드

  -  getter/setter 가 속성값에 직접 접근하지 않고, Backing Field를 통해 값을 저장하고 처리

 

by lazy

  : 처음 접근 시 초기화

  : val 속성에 사용

 

late init

  - not-null 변수에만 사용 가능

  - 기본 타입은 사용 불가

  - var 속성에 적용

 

singleton

  - class 대신 object 사용

  - java 에서는 MySingleton.INSTANCE 로 접근가능

 

Nothing 클래스

  - 생성 불가

  - 결코 존재할 수 없는 값(클래스?)을 나타내기 위해 사용

  - Object 같은 느낌인데, 생성불가한 클래스?

 

 

4장 함수형 프로그래밍

 

trailrec fun

- 꼬리 재귀호출 시, stack 프레임을 재사용하게해준다

- 컴파일 시 while 을 사용한 반복문으로 리팩토링됨

- 마지막 연산에서 자기 자신을 호출해야함

- try-catch-finally 블록 안에서 사용 불가

 

 

5장 컬렉션

 

배열

- val strings = arrayOf("apple", "banana")

- val nullStringArray = arrayOfNulls<String>(5)

- val squares = Arrays(5) { i -> (i * i).toString() }

- for ((index, value) in array.withIndex())

 

변경 불가능한 컬렉션

- listOf

- SetOf

- mapOf

 

변경 가능한 컬렉션

-  mutableListOf

-  mutableSetOf

-  mutableMapOf

 

컬렉션 to map

val keys = 'a' .. 'f'

val map = keys.associate { it to it.toString().repeat(5).capitalize() }

{ a = Aaaaa, b = Bbbbb, ...}

 

범위 값 제한

val range = 3..8

5.coerceIn(range)

5.coerceIn(3, 8)

- 범위에 속하는 경우 해당 값을 리턴하고, 그렇지 않은 경우 경계 값 리턴

 

같은 크기로나누기

val range = 0..10

val chunked = range.chunked(3)

chunked { it.sum() }

-  실제로 windowed 함수로 구현되어 있다

-  windowed(elementSize, step, partialWindows = true)

   : elementSize - 각 윈도우에 포함될 원소의 개수

   : step - 각 단계마다 전진할 원소의 개수

 

리스트 구조 분해하기

val (a, b, c, d, e) = listOf("a", "b", "c", "d", "e", "f")

 

정렬하기

data class Golfer(val score: Int, val first: String, val last: String)

golfers.sortedWith(compareBy( { it.score }, { it.last }, { it.first } )

 

컬렉션에서 특정 타입 원소들만 필터링하기

val strings = list.filterIsInstance<String>()

val strings = list.filterIsInstanceTo(mutableListOf<String>())

 

 

6장 시퀀스

 

- 컬렉션과 다르게 lazy 처리된다

- 자바 스트림과는 다르게 코틀린의 일부 시퀀스는 여러 번 순회할 수 있다

 

Short Circuit

- 특정 조건에 다다를 때까지 오직 필요한 데이터만을 처리하는 방식

 

시퀀스 생성하기

- val sequence1 = sequenceOf(1, 2, 3, 4)

- val sequence2 = listOf(1, 2, 3, 4).asSequence()

- generateSequence(seed) {nextFunction}

- takeWhile() : 특정 조건에 대해서 동작한다

 

 

7장 영역 함수

-  apply() : 객체 생성 시, 생성자 외에 초기화 동작이 필요할 때 사용 가능

-  also() : 코드 흐름을 방해하지 않고 부수적인 동작이 필요할 때 사용 가능

-  let() : null 이 아닌 레퍼런스에 대해 코드를 수행하고 null 인 경우에는 기본 값 리턴 가능

          : 연산 결과를 임시 변수에 할당하지 않고 처리하고 싶을 때

 

 

8장 코틀린 대리자

 

-  대리자를 사용해서 합성 구현하기

class SmartPhone (

    private val phone: Dialable = Phone(),

    private val camera: Snappable = Camera()

) : Dialable by phone, Snappable by camera

- by 키워드는 포함된 객체에 있는 모든public 함수를 이 객체를 담고 있는 컨테이너를 통해 노출할수 있다

 

lazy 대리자 사용하기

- 사용되는 시점에 초기화 수행

- val test: Int by lazy (mode: LazyThreadSafetyMode) { initializer }

- SYNCHRONIZED : 오직 하나의 스레드만 Lazy  인스턴스를 초기화할 수 있게 락을 사용

- PUBLICATION : 초기화 함수가 여러 번 호출될 수 있지만 첫 번째 리턴값만 사용

- NONE : Lock 이 사용되지 않음

 

notNull 대리자 사용하기

- var shouldNotBeNull: String by Delegates.notNull<String>()

- 속성 초기화 전에 접근하는 경우에 Exception 을 발생시킴

 

observable 대리자

- var watched: Int by Delegates.observable(1) { prop, old, new ->  }

- 값이 변경될 때 동작을 수행할 수 있다

 

vetoable 대리자

- var checked: Int by Delegates.vetoable(0) { prop, old, new -> new >= 0 }

- 값이 변경되기 전에 조건을 확인하여 값의 변경을 방지할 수 있다

 

Map 대리자

data class Project (val map: MutableMap<String, Any?>) {

    val name: String by map

    val priority: Int by map

    var completed: Boolean by map

}

- map 에 k,v 가 3쌍이 있고, 각각의 value 값이 property 로 대입된다.

- Project(mutableMapOf("name" to "Kotlin", "priority" to 5, "completed" to true))

 

 

9장 테스트

 

테스트 클래스 수명주기 설정하기

- Junit5 에서 @TestInstance(TestInstance.Lifecycle.PER_CLASS) 설정

- 위와 같이 설정하면 companion object 에 @BeforeClass 추가없이 테스트 객체를 한번만 생성하도록 사용할 수 있음

- 아래와 같은 방법으로 각 테스트 클래스에 매번 선언할 필요 없이 설정가능하다

   - src/test/resource/junit-platform.properties 파일에

      junit.jupiter.testinstance.lifecycle.default = per_class

 

데이터 클래스 테스트

- Junit5 의 assertAll() 을 사용하면 data 클래스의 여러 property 의 값을 한번에 검증할 수 있다

- 1번째 assertion 이 실패하더라도 나머지 assertion 을 모두 수행한다

- 이보다 assertion 하나를 이용해 2개 객체 간의 값을 비교할 수 있다

val book = service.findBookById("1555")

val expected = Book(isbn = "1555", title = "hello")

assertThat(book, is(expected))

 

 

10장 입력/출력

 

use 로 리소스 관리하기

- try-with-resources 가 지원되지 않기에 use() 혹은 File.useLines() 를 이용한다

 

 

11장 그 밖의 코틀린 기능

 

반복적으로 람다 실행하기

- repeat(5) { 코드 }

 

완벽한 when 강제하기

- when() {}.exhaustive

- else 구문을 작성하도록 강제한다

 

실행 가능한 클래스 만들기

- 아래 메소드 1개를 가진 클래스를 정의하면 실행가능하다

operator fun invoke() 

- val request = AstroRequest()

   val result = request()

 

경과 시간 측정하기

- var time = measureTimeMillis {}

 

쓰레드 사용하기

- thread {}

 

TODO 로 완성 강제하기

- TODO() 함수가 호출되면  Error 를 발생시킨다

 

함수 이름에 특수 문자 사용하기

- 백틱( `) 을 사용하면 공백을 추가할 수 있다

- 테스트에서만 사용한다

- 코드에서는 밑줄을 사용하는 것이 낫다

   

 

12장 스프링 프레임워크

- skip

 

13장 코루틴과 구조적 동시성

 

코루틴 빌더 선택하기

- runBlocking 빌더

   : 명령줄 검증 또는 테스트에 유용

   : 모든 내부 코루틴이 종료될때까지 현재 스레드를 블록

   : runBlocking {}

- launch 빌더

   : coroutineScope 의 확장 함수이다

   : 독립된 프로세스를 실행하는 코루틴을 시작하고, 해당 코루틴에서 리턴값을 받을 필요가 없을 때 사용

- async 빌더

   : coroutineScope 의 확장 함수이다

   : 값을 리턴해야 하는 경우에 사용한다

   : await() 를 통해 동작 완료를 기다린다

   : delay() 는 쓰레드를 중단하지 않고 코루틴을 대기 상태로 만드는 일시 중단 함수

- coroutineScope 빌더

   : 종료 전에 포함된 모든 코루틴이 완료될 때까지 기다리는 일시 중단 함수다

   : runBlocking 과 다르게 Main Thread 를 블록하지 않는다

 

async/await 를 withContext 로 변경하기

- async(Dispatcher.IO) {}.await()

- withContext(Dispatcher.IO) {}

 

디스패처 사용하기

- launch, async, withContext 의 인자로 추가하면 된다

- Dispatchers.Default : 공유 백그라운드 스레드 풀을 사용한다

- Dispatchers.IO : 파일 I/O 또는 블록킹 네트워크 I/O 같은 작업을 위해 설계된 on-demand 공유 풀을 사용한다

- Dispatchers.Unconfined : 일반 어플리케이션 코드에서는 사용해서는 안 된다.

- Dispatchers.Main : 안드로이드에서 UI 작업을 위해 사용한다 (안드로이드에서 추가로 제공)

    : kotlinx-coroutines-android 의존성을 추가해야 한다

 

자바 스레드 풀에서 코루틴 실행하기

- val dispatcher = Executors.newFixedThreadPool(10).asCoroutineDispatcher().use { }

 

코루틴 취소하기

- val job = launch {}

  job.cancel()  //동작을 취소한다

  job.join() // job 이 완료될때까지 기다렸다가 프로그램을 종료한다

- withTimeout(1000L) {}

- withTimeoutOrNull(1000L) {}

 

+ Recent posts