아래 소스 코드는 kotlinlang.org에서 테스트해볼 수 있습니다.
// 클래스는 파스칼 표기법(단어의 맨앞은 항상 대문자), 함수나 변수는 카멜표기법(맨앞의 문자는 소문자, 이후에는 대문자)
//
// package명은 일반적으로 회사도메인(com.youtube) + 프로젝트명 + 기능별
// package가 다르면, import 패키지명
// 자바와 달리, 클래스명과 파일명이 일치하지 않아도 됨
// scope - 패키지, 클래스, 함수
// 접근제한자 - public, internal, private, protected, package scope(public, internal, private), class scope(public, private, protected)
fun main() {
var a:Int = 10 //세미콜론 불필요
var ab = 11
val aa: Int = 20 //final
var b:Double = a.toDouble() //형변환
// val a = Person("케이", 1995)
// a = Person("조이", 1996) (x, 오류)
// val은 할당된 객체를 바꿀 수 없을 뿐이지, 객체 내부의 속성은 변경 가능
// 상수(constant) -> 절대 변경 불가능
// const val CONST_A = 1234, 상수로 선언하려면 기본자료형만 가능
// 클래스의 속성이나, 함수의 지역변수로는 선언할 수 없음
// 클래스 내부에서는 companion object로 선언 ->즉, 정적변수처럼
// 상수를 사용하는 이유 -> 변수의 경우, 런타임시, 객체를 생성하는데 시간이 더 소요되어 성능하락
// 메모리에 값을 고정하여 사용
// 늦은 초기화 -> lateinit var 변수는 초기값 할당 전까지 변수를 사용할 수 없음, 기본 자료형에는 사용할 수 없음
// ::sample.isInitialized로 확인
// lazy delegate properties -> 변수를 사용하는 시점까지, 초기화를 자동으로 늦춰줌 -> 코드의 실행시간을 최적화할 수 있는 코드
// val a:Int by layz{7}
var str = "너구리"
println(str)
//문자열
var test1 = "Test.Kotlin.String"
println(test1.length)
println(test1.toUpperCase())
println(test1.toLowerCase())
var test2 = test1.split(".")
println(test2)
println(test2.joinToString())
println(test2.joinToString("."))
println(test1.substring(5..10))
val nullString: String? = null // nullable로 만들려면 자료형 뒤에 '?'
val emptyString = ""
val blankString = " "
val normalString = "a"
println(nullString.isNullOrEmpty()) // false
println(emptyString.isNullOrEmpty()) // false
println(blankString.isNullOrEmpty()) // true
println(normalString.isNullOrEmpty()) // true
// isNullOrBlank() -> 공백도 null로 본다
var test3 = "kotlin.kt"
var test4 = "java.java"
println(test3.startsWith("java"))
println(test4.startsWith("java"))
println(test3.endsWith(".kt"))
println(test4.endsWith(".kt"))
println(test3.contains("lin"))
println(test4.contains("lin"))
// nullable 변수 사용
var sample:String? = null
if(sample != null) // null 상태로 속성이나 함수를 쓰려고 하면, exception 발생, 하지만, 이처럼 일일히 조건문으로 체크하면 귀찮음 -> "?.", "?:", "!!."
println(sample.toUpperCase())
println(sample?.toUpperCase()) // null
println(sample?:"default_value".toUpperCase()) // DEFAULT_VALUE
// println(sample!!.toUpperCase()) // exception
var sample_2 = "Kotlin"
sample_2?.run {
println(toUpperCase())
println(toLowerCase())
}
// 배열
var arr = arrayOf(1, 2, 3, 4, 5)
var nullarr = arrayOfNulls<Int>(5) //qoduf, generic
nullarr[1] = 10
println(nullarr[1])
println(arr[3])
//함수
println(add(4, 11))
//조건문
if(a > 8) {
println("크다")
} else {
println("작다")
}
// is, !is 연산자 -> 자료형이 맞는지 체크, 형변환까지 한번에
// a is Int
doWhen(a)
doWhen(a)
//반복문
while(a>5) {
print(a--)
}
var bb=10
do {
println(bb--)
} while(bb>5)
for(i in 0..9 step 2) {
print(i)
}
println()
for(i in 7 downTo 3) {
print(i)
}
println()
// break, continue
for(i in 1..10) {
if(i==2) continue
if(i==4) break
print(i)
}
println()
// 다중반복문
loop@for(i in 1..10) {
for(j in 1..10){
if(i==3 && j==4) break@loop //레이블이 달린 반복문을 기준으로 즉시 break시켜줌
println("i: $i, j: $j")
}
//다른 언어에서는 또 체크를 해줘야, 반복문이 모두 종료됨, but, 코틀린에서는......
}
// &&, ||, !
//클래스 - 속성(정적변수) + 함수
var person_1 = Person("부람이", 2)
println("안녕하세요. 저는 '${person_1.name}'입니다. 나이는 ${person_1.age}세 입니다.")
person_1.introduce()
var person_2 = Person("너구리")
var pet_1 = Animal("대박이", 4, "새")
var pet_2 = Dog("멍멍이", 3)
pet_1.introduce()
pet_2.introduce()
pet_2.bark()
var person_3 = Player()
person_3.run()
person_3.eat()
bbb(::aaa) //일반함수를 고차함수로 바꾸려면, 함수 이름 앞에 콜론을 두 개 붙임
// 파라미터로 넘길 함수를 굳이 이름까지 붙여 따로 만들 필요가 있을까? -> 람다함수
val ccc: (String)->Unit = {str -> println("$str")} //val ccc = {str:String->println("$str람다함수")}로 축약가능
// 위 람다함수 처럼, 파라미터가 1개 라면, {println(""$it")} 으로 축약가능
bbb(ccc)
val ddd: (Int, Int) -> Int = {a, b -> a+b}
var result = ddd(11, 22)
println(result)
val eee: () -> Unit = {println("no parameter")}
// 스코프 함수 (apply, run, with, also, let)
var book_1 = Book("반지의 제왕", 12000)
book_1.name = "[특가세일] "+ book_1.name // 스코프 함수를 사용하지 않는다면, 이처럼 속성과 함수를 사용해야 함
book_1.discount()
var book_2 = Book("해리포터", 12000).apply { // 참조연산자를 사용하여 apply를 붙이면, apply의 scope안에서 속성과 함수를 참조연산자 없이 사용 가능
name = "[특가세일] "+ name
discount()
}
book_2.run {
println("${name}, ${price}")
}
var book_3 = book_2.run { // 가격은 출력하지만, 마지막 구문인 book_2.name을 반환하여 book_3에 할당됨
println(book_2.price)
book_2.name
}
println(book_3)
// with는 run과 동일한 기능을 가지지만, 다만, 인스턴스를 참조연산자 대신, 파라미터로 받는다는 차이만 있을뿐
// book_2.run {...} -> with(book_2) {...}
// also, apply -> 처리가 끝나면 객체를 반환
// let, run -> 처리가 끝나면 최종값을 반환
// apply, run은 참조연산자 없이, 객체의 변수와 함수를 사용할 수 있었다면,
// also, let은 마치 파라미터로 객체를 넘긴 것처럼, it을 통해서 객체를 사용할 수 있음
// also, let이 굳이 파라미터를 통해서 인스턴스를 사용하는 귀찮을 과정을 거칠까?
// 같은 이름의 변수나 함수가 scope 밖에 중복되어 있는 경우에, 혼란을 방지하기 위해서
// object
// object는 별도로 객체를 생성하지 않음
// objec로 생성된 객체는 최초 사용시 자동으로 생성되며, 코드 전체에서 공용으로 사용됨
println(Counter.count)
Counter.countUp()
println(Counter.count)
Counter.clear()
println(Counter.count)
// companion object
// 클래스의 공용 속성 및 함수 = static 멤버
// 옵저버 패턴, 이벤트 리스너
EventPrinter().start() // EventPrinter의 객체를 생성하고, start() 함수만 호출
println()
// 업캐스팅, 다운캐스팅
var d1 = Drink()
d1.drink()
var d2:Drink = Cola()
d2.drink() //d2는 Drink 변수이므로, 이대로는 washDishes 함수를 호출할 수 없다
if(d2 is Cola) {
d2.washDishes()
}
var d3 = d2 as Cola //d3 뿐만이 아니라 d2도 다운캐스팅됨
d3.washDishes()
//Generic
UsingGeneric(A()).doShouting()
UsingGeneric(B()).doShouting()
doShouting(C())
var list = listOf("사과", "배")
for(fruit in list) {
println("${fruit}")
}
// 동일성 체크 (내용==, 객체===)
var sample_a = Product("호랑이", 4)
var sample_b = Product("호랑이", 4)
var sample_c = sample_a
var sample_d = Product("기린", 11)
println(sample_a == sample_b) //true
println(sample_a === sample_b) //false
println(sample_a == sample_c) //true
println(sample_a === sample_c) //true
println(sample_a == sample_d) //false
println(sample_a === sample_d) //false
// 디폴트 값
deliveryItem("짜장면")
deliveryItem("쌀", 2)
deliveryItem("피자", destination="학교")
// 매개변수 여러개 받을 때
println(sum_vararg(1, 2, 3, 4))
// infix함수 -> 연산자 처럼 사용할 수 있음, 아래 구현되어 있음
println(5 multiply 3) // 앞에 5는 객체 자신, 3이 파라미터 x
println(4.multiply(2))
// 중첩클래스(Nested Class), 중첩클래스는 외부 클래스의 내용을 공유할 수 없음, 즉 별개의 클래스
// 내부클래스 (Inner class), 외부클래스의 속성과 함수의 사용이 가능
Outer.Nested().introduce()
val outer = Outer()
val inner = outer.Inner() //내부클래스는 혼자서 객체를 만들 수 없고, 외부크래스의 객체가 있어야만 생성과 사용이 가능
inner.introduceInner()
inner.introduceOuter()
outer.text = "chaned outer class"
inner.introduceOuter()
// Data class와 일반 클래스 비교
val data_a = General("mike", 22)
println(data_a == General("mike",22))
println(data_a.hashCode())
println(data_a)
val data_b = Data("rudy", 3)
println(data_b == Data("rudy",3))
println(data_b.hashCode())
println(data_b)
val list_sample = listOf(Data("james", 11), Data("sam", 13))
// list_sample에 담긴 데이터를 for문을 통해 순회하려면
for((a, b) in list_sample) {
println("${a}, ${b}")
}
// Enum class
var state = State.SING
println(state)
state = State.SLEEP
println(state.isSleeping())
state = State.EAT
println(state.message)
}
//함수
fun add(a:Int, b:Int): Int { //뒤에 Int는 반환형
return a+b
}
//단일표현식 함수. 위의 함수를 간략히 표현
fun add_2(a:Int, b:Int) = a + b
//반환값이 없는 경우
fun add_3(a:Int, b:Int):Unit { //Unit은 생략가능
}
// when = switch
fun doWhen(a: Any) { // Any 최상위 자료형
when(a) {
10 -> println("a는 정수 10입니다")
is Int -> println("a는 Int자료형 입니다")
!is Int -> println("a는 Int자료형이 아닙니다")
else -> println("어떤 조건도 만족하지 않음")
}
}
fun doWhen_2(a: Any) { // Any 최상위 자료형
var result = when(a) {
10 -> "a는 정수 10입니다"
is Int -> "a는 Int자료형 입니다"
else -> "어떤 조건도 만족하지 않음"
}
println(result)
}
// 클래스
class Person(var name:String, var age:Int) {
init {
println("생성자 호출 : ${this.name}, ${this.age}")
}
constructor(name:String) : this(name, 5) { //보조생성자
println("보조생성자 호출")
}
fun introduce() {
println("안녕하세요. 저는 '${this.name}'입니다. 나이는 ${this.age}세 입니다.")
}
}
//상속, 코틀린은 상속금지가 기본값, 자식클래스는 부모클래스에 존재하는 속성(정적변수)과 같은 이름의 속성을 가질 수 없음
//자식 클래스가 호출될 때는, 반드시 부모클래스의 생성자까지 호출되어야 함
open class Animal(var name:String, var age:Int, var type:String) { //상속을 해주려면, 키워드 open 들어가야함
fun introduce() {
println("${this.name}, ${this.age}, ${this.type}")
}
open fun eat() { //자식클래스에서 오버라이딩 해주려면 키워드 open필요함
println("먹는다")
}
}
class Dog(name: String, age:Int) : Animal(name, age, "개") { //생성자에 들어가는 인자 앞에는 var를 붙이면, 속성으로 선언됨
fun bark(){
println("멍멍 짖는다")
}
override fun eat() { //부모클래스에 있는 함수를 오버라이딩 하는 경우, 키워드 override 필요함
println("게걸스레 먹는다")
}
}
//추상함수를 포함한 추상클래스(일부함수가 구현되지 않은 '미완성 클래스'->단독으로는 객체를 만들 수 없음)
abstract class Fruit {
abstract fun eat()
fun sniff() {
println("킁킁")
}
}
class Apple: Fruit() {
override fun eat() {
println("사각사각")
}
}
// 인터페이스, 생성자를 가질 수 없음, 구현부가 있는 함수->open함수로 간주, 구현부가 없는 함수->abstract함수로 간주
// 한번에 여러 인터페이스 상속 가능
interface Runner {
fun run()
}
interface Eater {
fun eat() {
println("음식을 먹습니다.")
}
}
class Player: Runner, Eater {
override fun run() {
println("재빠르게 뜁니다")
}
override fun eat() {
println("허겁지겁 먹는다")
}
}
// 고차함수 - 함수를 마치 클래스에서 만들어낸 객체처럼, 함수를 파라미터로 넘겨 줄 수도 있고, 결과값으로 반환받을 수도 있는 방법
fun aaa(str: String) {
println("$str 함수 aaa")
}
fun bbb(function: (String)->Unit) { // (자료형)->반환형, 반환형이 없는 경우 Unit
function("bbb가 호출한")
}
// 스코프 함수, 클래스의 객체를 scope함수에 전달하면, 객체의 속성이나 함수를 깔끔하게 불러 쓸 수 있음
// apply, run, with,also, let
class Book(var name:String, var price:Int) {
fun discount() {
price -= 2000
}
}
// object, 생성자 없이 직접 객체를 만들어냄, 즉 여러개의 객체가 필요하지 않은 경우에 사용
// 객체를 생성하지 않아도, 그 자체가 객체임
// Singleton Pattern -> 클래스의 객체를 단 하나만 만들어 사용하도록 하는 코딩 아키텍쳐 패턴
object Counter {
var count = 0
fun countUp() {
count++
}
fun clear() {
count = 0
}
}
// companion object
// 클래스의 공용 속성 및 함수 = static 멤버
class FoodPoll(val name:String) {
companion object { // 클래스 속성
var total=0
}
var count =0 // 각 객체의 속성
fun vote() {
total++
count++
}
}
// 이벤트가 일어나는 것을 감시, 함수로 직접 요청하지 않았지만, 시스템 또는 루틴에 의해 발생하게 되는 동작들 -> 이벤트,
// 이벤트를 즉각적으로 처리할 수 있도록 만드는 패턴을 '옵저버 패턴'이라고 부름 -> 구현하기 위해, 2개의 클래스 필요
// 이벤트를 수신하는 클래스(인터페이스를 구현하여 이벤트 발생 클래스에 넘겨줌), 이벤트를 발생 및 전달하는 클래스(자신의 이벤트를 받을 수 있는 인터페이스 공개)
// 인터페이스 -> '옵저버, 리스너'라고 부름, 이벤트를 넘겨주는 행위를 'callback'이라고 함
// 이벤트를 수신해서 출력하는 EventPrinter
// 숫자를 카운트하며, 5의 배수마다 이벤트를 발생시킬 Counter
// 위 2개의 클래스를 연결시킬, Interface, EventListener
interface EventListener { // 이벤트가 발생할 때, 숫자를 반환
fun onEvent(count:Int) // 추상함수, 리스너를 통해 이벤트를 반환하는 함수 이름은 관례적으로 'on(행위)'라는 규칙을 따름
}
class Counter2(var listener:EventListener) { // 이벤트가 발생하는 클래스
fun count() {
for(i in 1..100) {
if(i%5==0) listener.onEvent(i) // 5의 배수가 될 때마다, EventListener의 onEvent를 호출
}
}
}
// 이벤트를 받아서, 화면에 출력하는 클래스, EventListener를 상속하여 구현해야함
class EventPrinter:EventListener {
override fun onEvent(count: Int) {
print("${count}-")
}
fun start() {
val counter = Counter2(this)
counter.count()
}
}
// EventPrinter가 EventListener를 상속받아 구현하지 않고, 임시로 만든 별도의 EventListenr객체를 대신 넘겨줄 수 있음 -> 익명객체
// class EventPrinter { //상속받지 않음
// fun start() {
// val counter = Counter(object: EventListener) {
// override fun onEvent(count:Int) {
// print("$s{count}-")
// }
// }
// }
// }
// 다형성, 업캐스팅, 다운캐스팅
// 부모클래스->자식클래스 (Drink > Cola)
// var a:Drink = Cola() --> Cola 객체는 Drink로 업캐스팅하면 Drink의 기능만 사용하게 되고, (업캐스팅)
// var a:Cola = Cola() --> Drink와 Cola의 기능을 모두 사용할 수 있음 (다운캐스팅, 별도의 연산자 as와 is가 필요함)
//
// var a:Drink = Cola()
// a as Cola --> a는 Cola로 동작함
// var b = a as Cola
//
// 'is'는 변수가 자료형에 호환되는지를 먼저 체크한 후 변환해주는 캐스팅 연산자로 조건문에서 사용됨
//
// var a:Drink = Cola()
// if(a is Cola) {
//
// }
open class Drink {
var name = "음료"
open fun drink() {
println("${name}를 마십니다")
}
}
class Cola:Drink() {
var type = "사이다"
override fun drink() {
println("${name}중에 ${type}을 마십니다")
}
fun washDishes() {
println("${type}로 설거지를 합니다.")
}
}
// 제너릭
// 클래스 A > 클래스 B (상속)
// fun castingExam(var a:A) --> B를 넣어도 A로 캐스팅됨, A와 B의 인스턴스 모두 매개변수로 사용가능
// casting은 프로그램의 속도를 저하시킴 -> Generic 등판
// generic을 특정한 부모 클래스를 상속받는 클래스 타입으로만 제한하려면, <T:부모클래스>
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 <T: A> doShouting(t:T) {
t.shout()
}
// 동일성 체크 (내용==, 객체===)
// 기본자료형에는 equals()함수가 구현되어 있지만,custom클래스의 경우에는 별도로 구현해야 함
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
}
}
}
// overloading
// 같은 scope안에서 이름이 같더라도, 파라미터가 다르면 가능
// 디폴트 값
fun deliveryItem(name:String, count:Int=1, destination:String="집") {
println("${name}, ${count}, ${destination}")
}
// vararg,variable number of arguments, 같은 자료형을 개수에 상관없이 파라미터로 받고 싶을 때 사용
fun sum_vararg(vararg numbers: Int) : Int {
var sum = 0
for(n in numbers) {
sum += n
}
return sum
}
// 만약, vararg를 다른 자료형과 같이 쓰려면
// fun sample(text:String, vararg nums: Int) -> 맨 뒤에 위치해야 함
// infix함수 -> 연산자 처럼 사용할 수 있음
// class 안에서 infix함수를 선언할 때에는 클래스 이름은 쓰지 않는다
infix fun Int.multiply(x:Int):Int = this*x
// 중첩클래스(Nested Class), 중첩클래스는 외부 클래스의 내용을 공유할 수 없음, 즉 별개의 클래스
// 내부클래스 (Inner class), 외부클래스의 속성과 함수의 사용이 가능
class Outer {
var text = "Outer class"
class Nested { //샤용할 때에는, Outer.Nested()
fun introduce() {
println("nested class")
}
}
inner class Inner { //내부클래스는 혼자서 객체를 만들 수 없고, 외부크래스의 객체가 있어야만 생성과 사용이 가능
var text = "inner class"
fun introduceInner() {
println(text)
}
fun introduceOuter() {
println(this@Outer.text) //outer class와 inner class에 같은 이름의 속성이 있으므로, 이것처럼 참조
}
}
}
// Data class
class General(val name:String, val id:Int)
data class Data(val name:String, val id:Int)
// Enum class, Enumerated type, 열거형
enum class State(val message:String) {
SING("노래를 부릅니다"),
EAT("밥을 먹습니다"),
SLEEP("잠을 잡니다");
fun isSleeping() = this == State.SLEEP
}
// collection class (list, set, map)
// 리스트 (class Collection을 상속하는 서브클래스(List, Set, Map) 중에서 가장 단순한 형태)
// 두 종류 -> List<out T>, MutableList<T>
// List - > 생성시에 넣은 객체를 대체, 추가, 삭제 할 수 없음
// listOf(1, 2, 3), mutableListOf("a", "b", "c")
// list[index], mutableListOf -> add(data), add(index, data), remove(data), removeAt(index), shuffle(), sort()package
// 사용법은 배열과 거의 동일
// Set -> 집합, 순서 없음, index로 객체 참조 불가능
// sampleSet.contains("너구리")
// Set, MutableSet -> add, remove
// val a = mutableSetOf("orange", "apple", "pear")
// for(item in a) {
// println("$s{item}")
// }
// a.add("melon")
// println(a)
// Map -> key, value
// sampleMap["orange"]
// Map, MutableMap -> put, remove
// val a = mutableMapOf("레드벨벳" to "옴파옴파",
// "트와이스" to "fancy")
// for entry in a){
// println("${entry.key}, ${entry.value}")
// }
// a.put("오마이걸", "번지")
// println(a)
// println(a["트와이스"])
// collection 함수
// list, set, map과 같은 컬렉션에 일반 함수 또는 람다함수 형태를 사용
// lambda는 쓰고 버리는 일시적인 함수 입니다. 함수가 생성된 곳에서만 필요합니다.
// collection.forEach {
// println(it) // 순서대로 반복출력
// }
// collection.filter {
// it<4 // 조건에 맞는 collection을 다시 만들어서 반환
// }
// collection.map {
// it*2 // 연산 후의 collection을 반환
// }
// collection.any{it==0} // 하나라도 조건에 맞으면 true
// collection.all{it==0}
// collection.none{it==0}
// collection.count() // 갯수 반환
// collection.count(it>7) // 조건에 맞는 아이템 갯수 반환
// val nameList = listOf("박수영", "김지수", "김다현", "신유나")
// nameLsit.forEach {print(it + " ")}
// println()
// println(nameList.filter{it.startsWith("김")})
// println(nameList.map{"이름: "+ it})
// println(nameList.any{it=="김다현"})
// println(nameList.all{it.length==3})
// println(nameList.count{it.contains("지")})
// associateBy -> 아이템에서 key를 추출하여 map으로 변환하는 함수
// groupBy -> key가 같은 아이템끼리 배열로 묶어 map으로 만드는 함수
// partition -> 아이템에 조건을 걸어 두개의 컬렉션으로 나누어 줌
// data class Person(val name:String, val birthYear:Int)
// val personList = listOf(Person("유나", 1992), Person("조이", 1996), Person("쥬", 1999), Person("유나", 2003))
// println(personList.associteBy{it.birthYear})
// println(personList.groupBy{it.name})
// val (over98, under98) = personList.partition{it.brithYear>1998}
// println(over98)
// println(under98)
// flatMap -> 아이템마다 만들어진 컬렉션을 합쳐서 반환하는 함수
// getOrElse -> 인덱스 위치에 아이템이 있으면 아이템을 반환하고, 아닌 경우, 지정한 기본값을 반환하는 함수
// zip -> 컬렉션 두 개의 아이템을 1:1로 매칭하여 새 컬렉션을 만들어줌
// val nambers = listOf(-3, 7, 2, -4, 5)
// println(numbers.flatMap{listOf(it*10, it+10)})
// println(numbers.getOrElse(1){50})
// val names = listOf("a", "b", "c", "d", "e")
// println(names zip numbers)
// coroutine, 비동기 처리 -> 여러개의 루틴을 동시에 처리하는 방법
// 기존의 방법은 순서대로 동기적으로 실행하였음
// import kotlinx.coroutines.* -> 사용시, 삽입해야함
// GlobalScope -> 프로그램 어디서나 제어, 동작
// CoroutineScope -> 특정한 목적의 dispatcher를 지정하여 제어, 동작
// CoroutineScope를 만들때 적용가능한 dispatcher -> Default(기본적인 백그라운드 동작), IO(I/O에 최적화된 동작)
// launch vs async -> 반환값 여부 (반환값이 없는 Job객체, 반환값이 있는 Deffered객체)
// cancel, withTimeoutOrNull()
import kotlinx.coroutines.*
fun main() {
val scope = GlobalScope
scope.launch { // 실행이 되지 않는다 -> coroutine이 실행도 되기 전에, 메인 프로세스가 종료
for(i in 1..5) {
println(i)
}
}
}
//안드로이드, 메인스레드에서 runBlocking을 걸어주면, 일정 시간 이상 응답이 없는 경우, ANR(응답 없음 오류)->강제종료
fun main() {
runBlocking { // 루틴의 대기를 위한 추가적인 함수 -> delay(), join(Job객체에서 실행이 끝날때까지 대기), await(Defferred객체에서 실행이 끝날때까지 대기, 결과값도 반환함)
launch {
for(i in 1..5) {
println(i)
}
}
}
}
fun main() {
runBlocking {
val a = launch {
for(i in 1..5) {
println(i)
delay(10)
}
}
val b = async {
"async 종료"
}
println("async 대기")
println(b.await())
println("launch 대기")
a.join()
println("launch 종료")
}
}
fun main() {
runBlocking {
var result = withTimeoutOrNull(50) { //제한시간 내에 수행되면 결과값을 아닌 경우 null을 반환
for(i in 1..10) {
println(i)
delay(10)
}
"finish" // 키워드 return을 써주지 않아도 된다?
}
println(result)
}
}
아래 유투브 강의를 참고하여 정리하였습니다.
반응형