아래 소스 코드는 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)
    }
}

 

아래 유투브 강의를 참고하여 정리하였습니다.

youtu.be/8RIsukgeUVw

반응형

+ Recent posts