2021. 8. 9. 20:26ㆍKotlin
참고:
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) { }
'Kotlin' 카테고리의 다른 글
브라우저에서 Kotlin 테스트하기 (0) | 2021.10.26 |
---|---|
url로 부터 데이터 가져와 ui에 나타내기 (0) | 2021.07.08 |
Java로 layout_gravity와 gravity 설정하는 법 (0) | 2020.01.05 |