Kotlin 문법 정리

2021. 8. 9. 20:26Kotlin

참고:
https://youtube.com/playlist?list=PLQdnHjXZyYadiw5aV3p6DwUdXV2bZuhlN
더보기
fun main() {
    // 변수
    var a : Int? = null // null값 이라는 걸 알려주기 위해 
    var b : Long = 123L // b.toInt() --> Int형으로 변환
    var c = 0xABCD
    
    // 배열 (크기 한정 O)
    // Generic= <>내에 배열의 자료형
    var intArr = arrayOf(1,2,4)
    var intArr2 = arrayOfNulls<Int>(3)
    
    // Test
    println(add(1,2,3))
    doWhen(b)
    doWhen(c)
    
    // for(i in 0..9 step 2) { // 02468
    // for(i in 9 downTo 0) { // 9876543210
    for(i in 0..9) { // 0123456789
        print(i)
    }
    
    var p = Person("MoMo", 14)
    var p2 = Person("NaNa")
    
    var cat = Cat("네모냠")
    print(cat.name)
    var rabbit = Rabbit()
    rabbit.eat()
    var dog = Dog()
    dog.eat()
}

// 단일 표현식 함수 single-expression function
// 값을 더하는 기능을 가진 변수
fun add(a:Int, b:Int, c:Int) = a+b+c

fun doWhen(a: Any) { // Any : Kotlin 최상위 클래스
    // 1. 조건문으로서 when
    when(a) {
        1 -> print("정수 1")
        "momo" -> print("모모")
        
        // 여러개와 맞아도 1번째만 O
        is Long -> print("Long타입")
        !is String -> print("String아님")
        else -> print("어떤 것과도 조건이 맞지 않음")
    }
    
    // 2. 조건이 맞을 때 (동작대신)값을 반환하는, 표현식으로서 when
    var res = when(a) {
        1 -> "정수 1"
        "momo" -> "모모"
        is Long -> "Long타입"
        !is String -> "String아님"
        else -> "어떤 것과도 조건이 맞지 않음"
    }
    println(res)
}

// < class > = 인스턴스를 만드는 틀
// default: 상속불가한 클래스
class Person(
    // Property : 속성. 클래스에 선언된 변수
    var name: String, val age: Int) {
    fun introduce() { // 자주 사용되는 함수
        println("기본생성자!! ${name} ${age}")
    }
    
    // 기본생성자 (생성시 실행됨)(속성 초기화)
    init { introduce() }
    // 보조생성자 (기본생성자 호출= this)
    constructor(name: String) : this(name, 14) {
        println("보조생성자 '-'? ${name} ${age}")
    }
}

// open class: 상속가능한 클래스
open class Animal(var name: String, type: String)
class Cat(name: String) : Animal(name, "고양이")

// abstract class: 추상화된 클래스
abstract class LivingThing {
    abstract fun eat()
}
class Rabbit : LivingThing() {
    override fun eat() {
        println("마시쪙")
    }
}

// interface
interface Runner { fun run() }
interface Eater { fun eat(){ print("냠냠") }}
class Dog : Runner, Eater {
    override fun run() { print("달리는중") }
    override fun eat() { print("냠냠냠냠") }
}

# 12

모듈 module

    프로젝트는 여러 개의 모듈로 이루어짐
    모듈에는 큰 틀로 묶어둔 걸로, 여러 파일들이 존재
    예를 들어, 'package com.hs.data'
코틀린은 자바와 달리, 다른폴더에 위치해도 같은패키지명으로 사용가능

같은 패키지 내에 변수,함수,클래스 공유
다른 패키지소스를 가져오고 싶으면 'import com.hs.model' 또는 참조연산자로 접근


# 13

스코프 Scope

: 변수, 함수, 클래스의 '공용 범위'를 제어하는 단위

    (패키지 내부, 클래스 내부, 함수 내부)

    하위스코프에서는 상위스코프 재정의 가능

 

접근제한자 Access Modifier

: 스코프 외부에서 내부로의 접근을 제어

이를 개발자가 제어할 수 있도록 하는 기능

스코프 외부에서 건드리지 말아야할 값들을 제한하는 용도로 사용

    (public, internal, private, protected)

 

    패키지 스코프에서는 

    - public (default): 어떤 package에서도 접근가능

    - internal : 같은 module내에서만 접근가능

    - private : 같은 file내에서만 접근가능

 

    클래스 스코프에서는 

    - public (default) : class외부에서 접근가능

    - private : class 내부에서만 접근가능

    - protected : 자기 class와 상속받은 class에서 접근가능

 

# 14

고차함수

: 함수를 클래스에서 만든 인스턴스처럼 취급하는 방법

함수를 매개변수로 넘겨줄 수 있고, 결과값으로 반환받을 수도 있음

 

1. 함수를 매개변수로 넘겨주기

여기서 함수의 자료형은 어떻게 표현할까?

(함수의 매개변수 자료형들, , ...) -> 함수의 반환 자료형

ex. (String)->Unit

위와 같은 형태의 함수는 모두 매개변수로 받을 수 있음

 

2. 결과값으로 반환받기

고차함수 형태로 매개변수를 넘기려면, 함수명앞에 ::연산자 붙이면 됨

더보기
fun main() { 
    b(::a) // b가 호출한 함수 a
}

fun a(str: String) {
    println("$str 함수 a")
}
fun b(func: (String)->Unit) { // 고차함수
    func("b가 호출한")
}

 

람다함수

: 이름이 따로 붙지 않는 고차함수

  함수를 람다식으로 표현한 것

  그 자체가 고차함수라, 별도의 연산자 없이 변수에 담을 수 있음

  컬렉션의 조작, 스코프 함수의 사용에도 도움이 됨

 

여러 줄일 경우, 마지막 구문의 결과값을 반환

매개변수가 0개이면, ' ()->반환자료형 '형태

매개변수가 1개이면, 선언없이 it으로 사용가능

더보기
fun main() {     
    // 람다함수
    val c: (String)->Unit = { str: String ->
    	println("$str 람다함수")
    }
    b(c) // b가 호출한 람다함수
}

 

 

스코프 함수

    : 함수형 언어의 특징을 좀 더 편리하게 사용할 수 있도록 기본 제공하는 함수 

    예를 들어, 클래스의 인스턴스를 스코프 함수에 전달하면, 좀 더 깔끔한 처리가능

    (apply, run, with, also, let)

 

    apply : 인스턴스 생성 후, 변수에 담기 전에 초기화 과정에서 많이 사용

    run : apply와 같지만, 람다함수처럼 마지막 줄의 결과값을 반환 

             인스턴스 생성 후에, 인스턴스의 함수/속성을 스코프 내에서 사용해야 할 때 유용

    with : run과 같지만, 인스턴스를 매개변수로 받음

 

    also/ let : 각각 apply/ run과 같은 기능을 가짐 

                  apply/ run은 인스턴스에 대한 연산을 진행하였다면

                  also/ let은 매개변수로 인스턴스를 넘긴 것처럼 it으로 인스턴스 사용가능 

                  같은 이름의 변수/함수가 스코프 바깥에 중복되어 있는 경우에 혼란방지를 위함 

                  스코프 내에 있는 변수보다 높은 우선순위의 중복된 변수가 있다면, 예상치못한 결과 발생

더보기

ex.

fun main() { 
    // 생성한 인스턴스 
    // + .(참조연산자) 
    // + apply (스코프함수)(인스턴스 자신을 반환)
    // + {} (람다함수)
    var a = Book("코틀린 책", 10000).apply {
        // 생성되자마자 조작하기 위함
        // 원래는 참조연산자로 수행할 것을 좀 더 편하게 초기화
        name = "새로나온 코틀린 책"
        discount()
    }
    
    var price = 5000
    // 새로나온 코틀린 책 , 8000
    println("${a.name} , ${a.price}") 
    
    a.run {   // 새로나온 코틀린 책 , 5000
        println("$name , $price")
    }
    with(a) { // 새로나온 코틀린 책 , 5000
        println("$name , $price")
    }
    a.let {   // 새로나온 코틀린 책 , 8000
        println("${it.name} , ${it.price}")
    }
}

class Book(var name: String, var price: Int) {
    fun discount() { price -= 2000 }
}

 

# 16

object

: 생성자없이 직접 객체를 직접 생성 

여러 개의 인스턴스 객체가 필요없고, 단 하나의 객체에서 공통적인 속성/함수 사용

싱글톤 패턴(클래스 인스턴스를 단 하나만 만들어 사용하는 아키텍처 패턴)을 언어 차원에서 지원한 것 

최초 사용시에 자동으로 생성되어, 이후에는 코드 전체에서 공용으로 사용됨 

더보기
fun main() { 
    println(Counter.count) // 0
    
    Counter.countUp()
    Counter.countUp()
    println(Counter.count) // 2
    
    Counter.clear()
    println(Counter.count) // 0
}

object Counter {
    var count = 0
    fun countUp() { count++ }
    fun clear() { count = 0 }
}

 

companion object

: 클래스 내에 존재하면서, 기존 인스턴스들은 유지하되 공용 속성/함수를 별도로 만드는 기능 

java의 static멤버(클래스 내부에서 별도의 영역에 고정적으로 존재, 공용으로 사용가능한 속성/함수)와 같음 

더보기
fun main() { 
    var a = FoodPoll("짜장")
    var b = FoodPoll("짬뽕")
    a.vote(); a.vote()
    b.vote(); b.vote(); b.vote()
    
    println("${a.name} : ${a.count}") // 짜장 : 2
    println("${b.name} : ${b.count}") // 짬뽕 : 3
    println("총계 : ${FoodPoll.total}") // 총계 : 5
}

class FoodPoll(val name: String) {
    companion object {
        var total = 0 // 계속 누적
    }
    
    var count = 0
    fun vote() { total++; count++ }
}

 

# 17 

observer

: event가 일어나는 것을 감시하는 감시자의 역할

event= '키의 입력' 등과 같이 시스템/루틴에 의해 발생하게 되는 동작들 

 

observer 패턴

: event가 발생할 때마다 즉각적으로 처리할 수 있도록 하는 프로그래밍 패턴 

2클래스(event수신, event발생/전달) 간의 인터페이스(=리스너)가 observer

이렇게 event를 넘겨주는 행위를 callback이라고 함 

더보기
fun main() { 
    EventPrinter2().start() // 5-10-15-20-25-30 .. 100-
}

// Counter ~ EventPrinter
interface EventListener { fun onEvent(count: Int) }

// 을(노예) : 소통구를 매개변수로 받음
class Counter(var listener: EventListener) {
    // 하는 일
    fun count() { 
        for(i in 1..100) { if(i%5 == 0) listener.onEvent(i) }
    }
}

// 갑 : 할일 정의하는 인터페이스를 상속받음
class EventPrinter: EventListener {
    // 이벤트 처리 
    override fun onEvent(count: Int) { print("${count}-") }
    
    // 일 시킴
    fun start() { 
    	// this= EventPrinter인데, 상속된 EventListener형을 받음 
        val counter = Counter(this)
        counter.count()
    }
}
class EventPrinter2 {
    // 익명 객체 Anonymous Object = 
    // 이름이 없는 객체
    // 인터페이스를 상속받아 구현하지 않고, 임시로 만든 EventListener를 넘김 
    
    fun start() { 
        // 인터페이스를 구현한 객체를 코드 중간에도 즉시 생성하여 사용가능 
        val counter = Counter(object: EventListener {
            override fun onEvent(count: Int) { print("${count}-") }
        })
        counter.count()
    }
}

 

# 18

클래스는 상속을 통해 클래스를 확장할 수 있음

다형성 polymorphysm

: 콜라를 음료로 볼 수 있는 것

  음료를 상속하면 음료를 담을 때는 음료로!(Up-Casting) 콜라를 담을 때는 콜라로!(Down-Casting) 있을 수 있음

      Down-Casting할 때는 as, is연산자 필요 

          as= 변수를 호환되는 자료형으로 변환해주는 캐스팅 연산자

          is= 조건문 내에서 변수가 자료형에 호환되는지를 체크한 후 변환해주는 캐스팅 연산자  

 

: 클래스의 상속관계로 인스턴스의 호환성을 적극 활용할 수 있는 기능 

    - 수퍼클래스가 같은 인스턴스를 한 번에 관리

    - 인터페이스를 구현하여 사용하는 코드에서도 이용

더보기
fun main() { 
    var a = Drink()
    a.drink()
    
    var b: Drink = Cola() // 업캐스팅
    b.drink()
    
    // is연산자
    if(b is Cola) { // 호환되는지 확인(다운캐스팅)
        b.washDishes() // 일시적
    }
    
    // as연산자 (반환값 뿐아니라 변수자체도 다운태스팅)
    var c = b as Cola 
    c.washDishes()
    b.washDishes()
}

open class Drink {
    var name = "음료"
    open fun drink() { // override가능하도록 open
    	println("${name}를 마십니다")
    }
}
class Cola: Drink() {
    var type = "콜라"
    override fun drink() {
        println("${name}중에 ${type}을 마십니다")
    }
    fun washDishes() {
        println("${type}으로 설거지를 합니다")
    }
}

 

# 19

제너릭 Generic

: 클래스/함수에서 사용하는 자료형을 외부에서 지정할 수 있는 기능

클래스의 다형성을 이용하여 업캐스팅 클래스를 매개변수로 받아 사용가능하지만 속도저하 문제 있음 

함수/클래스 선언시에 실제 자료형으로 대체되는 type parameter<>를 받아 사용하는 방법

일반적으로 Type의 약자인 <T>로 사용, 여러 개일 경우 순서대로 <T, U, V>

특정 수퍼클래스로 제한하려면, <T: SuperClass>

 

ex.

fun <T> genericFunc(var param: T) {}

genericFunc<Int>(1)

genericFunc(1) // T = Int로 추론 

더보기
fun main() { 
    UsingGeneric(A()).doShouting()
    UsingGeneric(B()).doShouting()
    UsingGeneric(C()).doShouting()
    
    doShouting(B())
}

fun <T: A> doShouting(t: T) {
    t.shout()
}

open class A {
    open fun shout() { println("A가 소리칩니다") }
}
class B: A() {
    override fun shout() { println("B가 소리칩니다") }
}
class C: A() {
    override fun shout() { println("C가 소리칩니다") }
}
class UsingGeneric<T: A>(val t: T) {
    fun doShouting() { t.shout() }
}
fun main() { 
    UsingGeneric(A()).doShouting()
    UsingGeneric(B()).doShouting()
    
    doShouting(B())
}
fun <T: A> doShouting(t: T) {
    t.shout()
}

open class A {
    open fun shout() { println("A가 소리칩니다") }
}
class B: A() {
    // UsingGeneric를 거쳐 호출될 경우,
    // override안하면 A로 제한되어 A의 함수가 호출됨
    override fun shout() { println("B가 소리칩니다") }
}
class UsingGeneric<T: A>(val t: T) {
    fun doShouting() { t.shout() }
}

 

# 20 

리스트 List

Collection= 데이터를 모아 관리하는 클래스
하위로는 List, Set, Map 등이 있음 

List= 여러 개의 데이터를 원하는 순서대로 넣어 관리하는 형태
(List<out T>, MutableList<T>) ((mutable: 변할수있는))

더보기
// Collection= 데이터를 모아 관리하는 클래스
// 하위로는 List, Set, Map 등이 있음 

// List= 여러 개의 데이터를 원하는 순서대로 넣어 관리하는 형태
// (List<out T>, MutableList<T>) ((mutable: 변할수있는))

// 생성
listOf(1,2,3)
var b= mutableListOf('A','B','C')

// 추가
b.add('D')    // [A, B, C, D]
b.add(0, 'D') // [D, A, B, C, D]

// 삭제
b.remove('D') // [A, B, C, D]
b.removeAt(1) // [A, C, D]

// 정렬
b.shuffle() // 무작위 섞기
b.sort()    // 정렬 

// 확인
print(b[1]) // C

 

# 21 

String

더보기
val s1 = "Test.Kotlin.String"
s1.length //18
s1.toLowerCase() //test.kotlin.string
s1.toUpperCase() //TEST.KOTLIN.STRING
s1.substring(5..10) //Kotlin //시작..끝

var s2 = s1.split(".") // delimiter에 정규식,일반문자열 가능
s2  //[Test, Kotlin, String]
s2.joinToString()  //Test, Kotlin, String
s2.joinToString("-") //Test-Kotlin-String


val nullString: String? = null
val emptyString = ""
val blankString = " "
val normalString = "A"

// 비었으면 true (Space, Tab, LineFeed 등)
nullString.isNullOrEmpty()  // true
emptyString.isNullOrEmpty() // true
blankString.isNullOrEmpty() // false
normalString.isNullOrEmpty()// false

// blank도 빈걸로 보고 true
nullString.isNullOrBlank()  // true
emptyString.isNullOrBlank() // true
blankString.isNullOrBlank() // true
normalString.isNullOrBlank()// false


var s3 = "kotlin.kt"
var s4 = "java.java"

// 지정한 문자열로 시작하면 true
s4.startsWith("java")
// 지정한 문자열로 끝나면 true
s3.endsWith("kt")
// 지정한 문자열이 포함되면 true
s3.contains("lin")
// < null값 처리방법 >
//  - ?. (null safe operator)(null이면 끝)
//  - ?: (elvis operator)(null이면 대신 이걸사용)
//  - !!. (non-null assertion operator)

// 스코프함수와 함께 사용하면 좋음
var a: String? = "null"
a?.run { // null이 아니면
    println(toUpperCase()) // NULL 출력
}


// < 동일성 확인 >
//  - 내용의 동일성 (==)(equals())
//  - 객체의 동일성 (===)
var a = Product("콜라", 1000)
var b = Product("콜라", 1000)
var c = a

println(a==b)  //true
println(a===b) //false
println(a==c)  //true
println(a===c) //true


// 커스텀 class 사용시, equals()의 별도의 구현 필요
class Product(val name: String, val price: Int) {
    override fun equals(other: Any?): Boolean {
        if (other is Product) {
            return other.name==name && other.price==price
        } else { 
            return false 
        }
    }
}

 

# 23

더보기
fun main() {
    deliveryItem("집")
    deliveryItem("책", 3)
    deliveryItem("선물", destination= "친구 집")
    
    sum(1,2,3,4)
    
    println(6 multiply 4)
    println(6.multiply(4))
}

fun deliveryItem(name: String, count: Int= 1, destination: String= "집") {
    println("${name}, ${count}개를 ${destination}에 배달하였습니다.")
}

// 여러 개의 매개변수가 있을 경우, vararg형은 맨 뒤에 위치
fun sum(vararg numbers: Int) {
    var sum = 0
    for (n in numbers) {
        sum += n
    }
    println(sum)
}

// 함수를 자료형처럼 사용
// infix fun A자료형.함수이름(x: B자료형): 반환자료형
infix fun Int.multiply(x: Int): Int = this*x
// + class안에서 infix함수 선언시에는, 적용할 클래스가 자기자신이므로
// infix fun multiply(x: Int): Int = this*x 가 된다

 

# 24

내부 클래스에서 같은 이름의 속성/함수가 있다면, 'this@OuterClass.이름' 형태로 참조하면 된다.

 

# 25

data class

= equals(), 고유코드 생성하는 hashcode(), toString(), 객체를 복사해주는 copy(), 속성을 순서대로 반환하는 componentX() 가 자동 구현됨. 

 

enum class

더보기
fun main() {
    var state = State.SING
    println(state) // SING
    
    state = State.SLEEP
    println(state.isSleeping()) // true
    
    state = State.EAT
    println(state.message) // 밥을 먹습니다
}

enum class State(val message: String) {
    SING("노래를 부릅니다"),
    EAT("밥을 먹습니다"),
    SLEEP("잠을 잡니다"); // 속성값 끝이다
    
    fun isSleeping() = this == State.SLEEP
}

 

# 26

< Set >

생성: setOf(values), mutableSetOf(values)

추가: add(value)

삭제: remove(value)

있는지 확인: contains(value)

 

< Map >

생성: mapOf(key값 to value값, 등)

추가: put(key값, value값)

삭제: remove(key값)

확인: map.key, map.value, map[key]

 

# 27

Collection 함수

(List, Set, Map 등)

순회: collection.forEach { it }

조건에 맞는 객체만 반환: collection.filter { it <4 }

순서대로 값을 업데이트: collection.map { it*2 }

 

collection.any { it == 0 }  // 하나라도 조건에 맞으면 true

collection.all { it == 0 }    // 모두 조건에 맞으면 true

collection.none { it == 0 }  // 하나도 조건에 안맞으면 true

collection.count { it > 7 } // 조건에 맞는 아이템 개수 반환 

 

다음은 조건에 맞는 객체가 없는 경우, NoSuchElementException 발생

collection.first { it > 3 }   // 조건에 맞는 첫번째아이템 반환

collection.last { it > 3 }   // 조건에 맞는 마지막아이템 반환

collection.find { it > 3 }   // 조건에 맞는 첫번째아이템 반환

collection.findLast { it > 3 }   // 조건에 맞는 첫번째아이템 반환

 

다음은 조건에 맞는 객체가 없는 경우, null을 반환

collection.firstOrNull { it > 3 }   // 조건에 맞는 첫번째아이템 반환

collection.lastOrNull { it > 3 }   // 조건에 맞는 첫번째아이템 반환

 

# 28

// 아이템에서 key추출하여 map으로 변환

collection.associateBy { it.name }

 

// 특정한 값을 지정하여 그룹끼리 묶어 map으로 반환

collection.groupBy { it.birthYear }

 

// 조건을 걸어 true/false로 나누어 Pair로 반환 (.first/.second로 참조)

collection.partition { it.birthYear > 2000 }

 

// 아이템마다 만들어진 collection을 합쳐 반환

collection.flatMap { listOf(it*3, it+3) }

 

// index위치에 아이템있으면 반환, 없으면 {}안의 값 반환

collection.getOrElse(8) { "50" }

 

// collection 2개의 아이템을 1대1로 매칭하여 List<Pair>반환 (크기가 다르면, 작은값으로)

collectionA zip collectionB

 

# 29

val

= 할당된 객체 변경불가이지만, 객체 내부의 속성 변경가능 

 

const val

= 상수. 기본자료형만 가능. 일반적으로 상수명은 대문자/언더바(_)만 사용.

companion object 아래에 객체의 생성과 관계없이 고정적인 값으로 사용.

 

lateinit var

= 초기값 할당 전까지는 변수사용불가. 기본자료형에는 사용불가. 

::a.isInitialized  // 초기화하였는지 확인 (true/false)

 

by lazy { }

= (lazy delegate properties)변수를 사용하는 시점까지 초기화를 늦춰 시행.

여러 줄 수행가능. 맨 마지막 값이 반환.

 

더보기
fun main() {
    val foodCourt = FoodCourt()
    foodCourt.searchPrice(FoodCourt.FOOD_HAMBURGER)
    foodCourt.searchPrice(FoodCourt.FOOD_PASTA)
    foodCourt.searchPrice(FoodCourt.FOOD_PIZZA)
}

class FoodCourt {
    fun searchPrice(foodName: String) {
        val price = when(foodName) {
            FOOD_PASTA -> 13000
            FOOD_PIZZA -> 25000
            FOOD_HAMBURGER -> 15000
            else -> 0
        }
        println("${foodName}의 가격은 ${price}원 입니다")
    }
    
    companion object {
        // 변수를 사용하는 것보다 속도 빠름 
        const val FOOD_PASTA = "파슷하"
        const val FOOD_PIZZA = "핏즈아"
        const val FOOD_HAMBURGER = "햄벅어"
    }
}

 

# 30

비트 연산

= 정수형 변수를 2진법으로 연산. 정수형의 값을 bit단위로 나누어 data를 좀 더 작은 단위로 담아 경제성을 높이기 위해 사용.

32비트에 서로 다른값 넣기, 5비트/27비트 따로 서로 다른 값 넣기, 변수 하나에 여러 개의 값을 담아 사용.

 

(bitwise shift operators)(비트를 밀어주는 연산자)

shl // shift left. 부호비트(최상위bit)를 제외한 모든bit를 좌측으로 밀어

shr // shift right

ushr // unsigned shift right. 부호비트를 포함하여 우측으로 밀어

 

(bitwise operators)

and // 원하는 위치에 비트값 확인(1), 비트값 clear(0)

or    // 원하는 위치에 비트값을 1로 설정(1),

xor  // 같으면 0. 내가 원하는 값과 같은지 확인.

inv() // 비트값 모두 반전

 

더보기
var bitData: Int = 0b10000

bitData = bitData or (1 shl 2)
println(bitData.toString(2)) // 10100

bitData = bitData and (1 shl 4)
println(bitData.toString(2)) // 10000

println(bitData shr 4) // 1

bitData = bitData and ((1 shl 4).inv())
println(bitData.toString(2)) // 0

println((bitData xor (0b10100)).toString(2)) // 10100

 

# 31 (참고: https://youtu.be/Lpieg1zrKdg)

코루틴

= 비동기처리. 

메인이 되는 루틴, 별로도 진행이 가능한 루틴으로 개발자가 루틴의 실행/종료를 마음대로 제어할 수 있는 단위.

사용하기 위해서는, import kotlinx.coroutines.*

 

제어범위, 실행범위 지정 가능(=Scope)

- GlobalScope: program 어디서나 제어/동작 가능한 기본범위

- CoroutineScope: 특정한 목적의 Dispatcher를 지정하여 제어/동작 가능한 새로운 범위

       Dispatchers.Default: 기본적인 백그라운드 동작

       Dispatchers.IO        : I/O에 최적화된 동작

       Dispatchers.Main   : 메인(UI) 스레드에서 동작

       + Dispatcher는 플랫폼에 따라 지원되지 않는 Dispatcher도 있음 

 

코루틴은 위와 같은 Scope내에서 생성 가능.

val scope = CoroutineScope(Dispatcher.Default)

val coroutineA = scope.launch {}  // 반환값이 없는 Job객체

val coroutineB = scope.async {}   // 반환값이 있는 Deferred객체 (마지막 구문 반환)(호출될 때 시작)

 

위만 사용한다면 코루틴이 시작하기도 전에 종료될 수 있음.

runBlocking {  // 코루틴이 종료될 때까지, 메인루틴을 잠시 대기시킴 

  launch { }

  async { }

}

주의, Android Main Thread에서 runBlocking을 걸어주었을 때

일정 시간 이상 응답이 없는 경우, ANR(응답없음오류) 발생하여 앱이 강제종료됨. 

 

루틴의 대기를 위한 추가적인 함수

(코루틴 내부/runBlocking같은 루틴대기가 가능한 구문 안에서만 동작 가능)

delay(milisecond: Long) // milisecond단위로 루틴을 잠시 대기

Job.join()               // Job객체의 루틴이 끝날때까지 대기

Deferred.await()   // Deferred객체의 루틴이 끝날때까지 대기 (결과값 반환)

 

코루틴 실행 도중에 중단하는 함수

cancel()  // 다음 2가지 조건이 발생하며 코루틴이 중단

         1. 코루틴 내부의 delay()/yield()가 사용된 위치까지 수행된 뒤 종료

         2. cancel()로 인해 isActive속성이 false가 되는데, 이를 확인항여 수동으로 종료

 

// 제한시간 내에 수행되면 결과값을, 아니면 null을 반환 (blocking함수에 해당)(마지막 구문 반환)

withTimeoutOrNull(milisecond: Long) { }

 

 

반응형