반응형

Main 함수

fun main(args: Array<String>) {
    println("Hello World")
}

변수의 선언

val a: Int = 10     // 변경 불가능한 변수
var b: Int = 5      // 변경 가능한 변수
var c: Int? = null  // null을 가질 수 있는 변수

var d = "Hello World"
var e = """
    Hello
    World
""".trimIndent()

형 변환

var a: Int = 54321
var b: Long = a.toLong()

배열

var a = arrayOf(1, 2, 3, 4, 5)
var b = arrayOfNulls<Int>(5)

println(a[1])
println(b[1])

타입 추론

var a: Int = 54321  // 일반적인 사용
var b = 12345       // 타입 추론

함수

fun add(a: Int, b: Int): Int {
    return a + b
}
fun add(a: Int, b: Int) = a + b
var result = add(1, 2)

when

fun doWhen(a: Any) {
    when (a) {
        1 -> println("1")
        "Hello", "Hello World" -> println("Hello")
        is Long -> println("is long type")
        !is String -> println("is not string type")
        else -> println("else")
    }
}
fun doWhen(a: Any) {
    var result = when (a) {
        1 -> "1"
        "Hello", "Hello World" -> "Hello"
        is Long -> "is long type"
        !is String -> "is not string type"
        else -> "else"
    }

    println(result)
}
fun doWhen(a: Any) {
    val result = when {
        a == 1 -> "1"
        a in arrayOf("Hello", "Hello World") -> "Hello"
        a is Long -> "is long type"
        a !is String -> "is not string type"
        else -> "else"
    }

    println(result)
}
fun main() {
    doWhen(1)           // 1
    doWhen("Hello")     // Hello
    doWhen(12L)         // is long type
    doWhen(3.14159)     // is not string type
    doWhen("Kotlin")    // else
}

if

val number = 5
val status = if (number % 2 == 0) "EVEN" else "ODD"
val number = 5

if (number in 1..10) {
    println("1부터 10 사이의 숫자")
}
val value = 5

if (value is Int) {
    println("value는 숫자")
}
val value: String? = "ABCDE"

value?.takeIf { it.length >= 5 }?.let { println(it) } // 길이가 5보다 크므로 let 구문 실행
value?.takeUnless { it.length >= 10 }?.let { println(it) } // 길이가 10보다 크지 않으므로 let 구문 실행

for

// 0부터 9까지 1씩 증가
for (i in 0..9) {
    println(i)
}
// 0부터 9까지 3씩 증가
for (i in 0..9 step 3) {
    println(i)
}
// 9부터 0까지 1씩 감소
for (i in 9 downTo 0) {
    println(i)
}
// a부터 e까지 증가
for (i in 'a'..'e') {
    println(i)
}

흐름 제어

loop@for (i in 1..10) {
    for (j in 1..10) {
        // 조건에 해당 되면 loop라는 라벨의 루프를 종료(continue도 동일하게 사용 가능)
        if (i == 1 && j == 2) {
            break@loop
        }

        println("i : $i, j : $j")
    }
}

클래스 기본 구조

class Person(var name: String, val birthYear: Int)

fun main() {
    var a = Person("John", 1990)

    println("${a.name} : ${a.birthYear}")
}
class Person(var name: String, val birthYear: Int) {
    fun introduce() {
        println("${name} : ${birthYear}")
    }
}

fun main() {
    var a = Person("John", 1990)

    a.introduce()
}
class Person1 {
    var name: String? = null
}

class Person2 {
    var name: String? = null
        private set

    fun changeName() {
        name = "john"
    }
}

fun main() {
    val person1 = Person1()
    person1.name = "john"

    println(person1.name)

    val person2 = Person2()
    // person2.name = "john" // 불가능
    person2.changeName()

    println(person2.name)
}

클래스의 생성자

class Person(var name: String, val birthYear: Int) {
    init {
        // 기본 생성자 실행시 호출되는 부분
        println("${this.name} : ${this.birthYear}")
    }

    constructor(name: String) : this(name, 1997)
}

fun main() {
    var a = Person("John", 1990)
    var b = Person("Tom")
}

클래스의 상속

// 상속 받게 할 수 있도록 class 앞에 open 키워드 필요
open class Animal(var name: String, var age: Int, var type: String) {
    fun introduce() {
        println("${this.name}, ${this.age}, ${this.type}")
    }
}

class Dog(name: String, age: Int) : Animal(name, age, "개")

fun main() {
    var a = Animal("happy", 5, "개")
    var b = Dog("happy", 5)

    a.introduce()
    b.introduce()
}

오버라이딩

open class Animal {
    open fun eat() {
        println("음식을 먹습니다")
    }
}

class Tiger : Animal() {
    override fun eat() {
        println("고기를 먹습니다")
    }
}

fun main() {
    var a = Tiger()

    a.eat()
}

추상클래스

abstract class Animal {
    abstract fun eat()
}

class Rabbit : Animal() {
    override fun eat() {
        println("당근을 먹습니다")
    }
}

fun main() {
    var a = Rabbit()

    a.eat()
}

인터페이스

interface Runner {
    fun run()
}

interface Eater {
    fun eat() {
        println("음식을 먹습니다")
    }
}

class Dog : Runner, Eater {
    override fun run() {
        println("달리는중")
    }

    override fun eat() {
        println("먹는중")
    }
}

fun main() {
    var a = Dog()

    a.run()
    a.eat()
}

고차함수

fun a(str: String) {
    println(str)
}

// Unit 리턴 타입 없음
fun b(function: (String) -> Unit) {
    function("Hello")
}

// ::는 고차함수를 지칭하는 키워드
fun main() {
    b(::a)
}

람다

fun b(function: (String) -> Unit) {
    function("Hello")
}

fun main() {
    val a: (String) -> Unit = { str -> println(str) }
//    val a = { str: String -> println(str) }

    b(a)
    b { str -> println(str) }
}
fun calc(a: Int, b: Int, calculator: (Int, Int) -> Int): Int {
    return calculator(a, b)
}

fun main() {
    println(calc(1, 2) { a, b -> a + b })
    println(calc(1, 2) { a, b -> a * b })
}

스코프 함수(Scope Function)

  • 인스턴스의 속성이나 함수를 스코프 함수 내에서 쉽게 접근해서 사용 가능

Extension Scope Function 예제

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

fun main() {
    // 컨텍스트 객체 참조 : this
    // 리턴 : 컨텍스트 객체
    val book1 = Book("Kotlin", 10000).apply {
        name = "[초특가] $name"
        discount()
    }

    // 컨텍스트 객체 참조 : it
    // 리턴 : 컨텍스트 객체
    val book2 = Book("Kotlin", 10000).also {
        it.name = "[초특가] ${it.name}"
        it.discount()
    }

    // 컨텍스트 객체 참조 : this
    // 리턴 : 람다식의 실행 결과
    val bookInfo1 = Book("Kotlin", 10000).run {
        "${name} : ${price}"
    }

    // 컨텍스트 객체 참조 : it
    // 리턴 : 람다식의 실행 결과
    val bookInfo2 = Book("Kotlin", 10000).let {
        "${it.name} : ${it.price}"
    }

    println("${book1.name} : ${book1.price}") // [초특가] Kotlin : 8000
    println("${book2.name} : ${book2.price}") // [초특가] Kotlin : 8000
    println(bookInfo1) // Kotlin : 10000
    println(bookInfo2) // Kotlin : 10000
}

Non-Extension Scope Function 예제

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

fun main() {
    val book = Book("Kotlin", 10000)

    // 컨텍스트 객체 참조 : this
    // 리턴 : 람다식의 실행 결과
    val bookInfo1 = with(book) {
        "${name} : ${price}"
    }

    // 컨텍스트 객체 참조 : 없음
    // 리턴 : 람다식의 실행 결과
    // 연산 과정을 한 블락으로 관리할 때 사용
    val bookInfo2 = run {
        "Kotlin : 10000"
    }

    println(bookInfo1) // Kotlin : 10000
    println(bookInfo2) // Kotlin : 10000
}

오브젝트

object Counter1 {
    const val NAME = "Counter1"
    var count = 0

    fun countUp() {
        count++
    }
}

class Counter2 {
    companion object {
        const val NAME = "Counter2"
        var count = 0

        fun countUp() {
            count++
        }
    }
}

fun main() {
    Counter1.countUp()
    Counter1.countUp()
    println("${Counter1.NAME}, ${Counter1.count}")

    Counter2.countUp()
    Counter2.countUp()
    println("${Counter2.NAME}, ${Counter2.count}")
}

클래스의 다형성

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}을 설거치를 합니다")
    }
}

fun main() {
    var a = Drink()
    var b: Drink = Cola()

    a.drink()
    b.drink()

    if (b is Cola) {
        b.washDishes()
    }

    var c = b as Cola
    c.washDishes()
    b.washDishes()
}

제네릭

open class Animal {
    open fun shout() {
        println("Animal이 소리칩니다")
    }
}

class Dog : Animal() {
    override fun shout() {
        println("Dog가 소리칩니다")
    }
}

class UsingGeneric<T: Animal>(var t: T) {
    fun doShouting() {
        t.shout()
    }
}

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

fun main() {
    var a = UsingGeneric(Animal())
    var b = UsingGeneric(Dog())

    a.doShouting() // Animal이 소리칩니다
    b.doShouting() // Dog가 소리칩니다

    doShouting(Dog()) // Dog가 소리칩니다
}

문자열

fun main() {
    val test1 = "Test.Kotlin.String"

    println(test1.length) // 18
    println(test1.toLowerCase()) // test.kotlin.string
    println(test1.toUpperCase()) // TEST.KOTLIN.STRING
    println(test1.substring(5..10)) // Kotlin

    val test2 = test1.split(".")

    println(test2.joinToString()) // Test, Kotlin, String
    println(test2.joinToString("-")) // Test-Kotlin-String
}
fun main() {
    val nullString: String? = null
    val emptyString = ""
    val blankString = " "
    val normalString = "A"

    println(nullString.isNullOrEmpty()) // true
    println(emptyString.isNullOrEmpty()) // true
    println(blankString.isNullOrEmpty()) // false
    println(normalString.isNullOrEmpty()) // false

    println(nullString.isNullOrBlank()) // true
    println(emptyString.isNullOrBlank()) // true
    println(blankString.isNullOrBlank()) // true
    println(normalString.isNullOrBlank()) // false
}

null operator

fun main() {
    var a: String? = null

    // null safe operator
    // 객체가 null이면 뒷 구문을 실행하지 않음
    println(a?.toUpperCase())

    // 아래 내용 실행되지 않음
    a?.run {
        println(toUpperCase())
    }

    // elvis operator
    // 객체가 null이면 : 다음 객체를 사용
    println(a?:"default".toUpperCase())

    // non-null assertion operator
    // null일 경우 NPE 발생하도록 의도적으로 방치
    println(a!!.toUpperCase())
}
fun printName(name: String?) {
    name ?: return
    println(name)
}

fun main() {
    printName(null) // do nothing
    printName("john") // "john"
}

내용 동일성, 객체 동일성 체크

class Product(val name: String, val price: Int) {
    override fun equals(other: Any?): Boolean {
        return other is Product && this.name == other.name && this.price == other.price
    }
}

fun main() {
    var a = Product("콜라", 1000)
    var b = Product("콜라", 1000)
    var c = a
    var d = Product("사이다", 1000)

    println(a == b) // true : equals() 실행 결과가 같은지 확인
    println(a === b) // false : 객체가 같은지 확인

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

    println(a == d) // false
    println(a === d) // false
}

default arguments

fun exam(name: String = "john", age: Int = 20) {
    println("${name}, ${age}")
}

fun main() {
    exam("tom")
    exam("tom", 30)
    exam(age = 30)
}

variable arguments

fun sum(vararg numbers: Int): Int {
    var sum = 0
    for (num in numbers) {
        sum += num
    }
    return sum
}

fun main() {
    println(sum(1, 2, 3))
}

infix function

infix fun Int.multiple(x: Int): Int = this * x

fun main() {
    println(6 multiple 4)
    println(6.multiple(4))
}

중첩 클래스

  • 외부 클래스의 내용을 중첩 클래스 안에서 공유할 수 없음
  • 외부 클래스의 생성 없이 사용 가능
class Outer {
    class Nested {
        fun introduce() {
            println("Nested Class")
        }
    }
}

fun main() {
    Outer.Nested().introduce()
}

내부 클래스

  • 외부 클래스의 내용을 내부 클래스 안에서 공유할 수 있음
  • 외부 클래스 생성 후 사용 가능
class Outer {
    var text = "Outer Class"

    inner class Inner {
        var text = "Inner Class"

        fun introduce() {
            println("${this@Outer.text}, ${text}")
        }
    }
}

fun main() {
    Outer().Inner().introduce()
}

Data Class

  • 데이터를 다루는데 최적화된 클래스
  • equals(), hashCode(), toString(), copy(), componentN() 함수를 자동으로 생성
class General(val name: String, val id: Int)

data class Data(val name: String, val id: Int)

fun main() {
    val a = General("john", 100)

    println(a == General("john", 100)) // false
    println(a) // General@6fadae5d

    val b = Data("tom", 200)

    println(b == Data("tom", 200)) // true
    println(b) // Data(name=tom, id=200)

    println(b.copy()) // Data(name=tom, id=200)
    println(b.copy(name = "jane")) // Data(name=jane, id=200)
    println(b.copy(id = 300)) // Data(name=tom, id=300)
}

분리 선언(Destructuring Declarations)

  • componentN() 함수를 활용하여 값을 분리하여 선언하는 방식
class Book(val name: String, val price: Int) {
    operator fun component1(): String {
        return name
    }

    operator fun component2(): Int {
        return price
    }
}

data class Person(val name: String, val age: Int)

fun main() {
    /*
        분리 선언시 다음 코드로 컴파일된다
        val a1 = person.component1()
        val b1 = person.component2()
    */
    val person = Person("john", 25)
    val (a1, b1) = person

    println("name : ${a1}, age : ${b1}") // name : john, age : 25

    val book = Book("Kotlin", 10000)
    val (a2, b2) = book

    println("name : ${a2}, price : ${b2}") // name : Kotlin, price : 10000
}
fun main() {
    /*
        Map.Entry는 기본적으로 아래처럼 componentN() 함수가 구현되어 있다.
        operator fun <K, V> Map<K, V>.iterator(): Iterator<Map.Entry<K, V>> = entrySet().iterator()
        operator fun <K, V> Map.Entry<K, V>.component1() = getKey()
        operator fun <K, V> Map.Entry<K, V>.component2() = getValue()
    */
    val map = mapOf(
        "john" to 25
    )

    for ((key, value) in map) {
        println("name : ${key}, age : ${value}") // name : john, age : 25
    }

    for ((_, value) in map) {
        println("age : ${value}") // age : 25
    }

    println(map.mapValues { entry -> entry.value + 10 }) // {john=35}
    println(map.mapValues { (key, value) -> value + 10 }) // {john=35}
    println(map.mapValues { (_, value) -> value + 10 }) // {john=35}
}

Private Setter

class Person1(_name: String) {
    var name = _name
        private set

    fun changeName() {
        name = "tom"
    }
}

class Person2(private var _name: String) {
    var name
        get() = _name
        private set(value) {
            _name = value
        }

    fun changeName() {
        name = "tom"
    }
}

data class Person3(private var _name: String) {
    var name = _name
        private set

    fun changeName() {
        name = "tom"
    }
}

data class Person4(private var _name: String) {
    var name
        get() = _name
        private set(value) {
            _name = value
        }

    fun changeName() {
        name = "tom"
    }
}

fun main() {
    val person1 = Person1("john")
    // person1.name = "tom" // 불가능
    person1.changeName()
    println(person1.name)

    val person2 = Person2("john")
    // person2.name = "tom" // 불가능
    person2.changeName()
    println(person2.name)

    val person3 = Person3("john")
    // person3.name = "tom" // 불가능
    person3.changeName()
    println(person3.name)

    val person4 = Person4("john")
    // person4.name = "tom" // 불가능
    person4.changeName()
    println(person4.name)
}

Enum Class

enum class State(val message: String) {
    EAT("먹다"),
    SLEEP("자다");

    fun isSleeping() = this == State.SLEEP
}

fun main() {
    println(State.EAT) // EAT
    println(State.SLEEP.isSleeping()) // true
}

List

fun main() {
    val a = listOf("사과", "딸기", "배")

    println(a[1])

    for (fruit in a) {
        println(fruit)
    }

    val b = mutableListOf(6, 3, 1)

    b.add(4) // 4 추가
    b.add(2, 8) // 2번 인덱스에 8값으로 교체
    b.removeAt(0) // 0번 인덱스 요소 제거
    b.shuffle() // 무작위로 섞기
    b.sort() // 정렬
}

Set

fun main() {
    val a = mutableSetOf("귤", "바나나")

    for (item in a) {
        println(item)
    }

    a.add("자몽")
    println(a) // [귤, 바나나, 자몽]

    a.remove("바나나")
    println(a) // [귤, 자몽]

    println(a.contains("귤")) // true
}

Map

fun main() {
    val a = mutableMapOf(
        "john" to 100,
        "tom" to 200
    )

    for (entry in a) {
        println("${entry.key} : ${entry.value}")
    }

    // 추가
    // a.put("jane", 300)
    a["jane"] = 300
    println(a) // {john=100, tom=200, jane=300}

    // 삭제
    a.remove("jane")
    println(a) // {john=100, tom=200}

    // 사용
    // println(a.get("john"))
    println(a["john"]) // 100
}

컬렉션 함수 - 1

fun main() {
    val nameList = listOf("john", "tom", "jane")

    nameList.forEach { print("${it} ") }
    println() //john tom jane

    println(nameList.filter { it.startsWith("j") }) // [john, jane]

    println(nameList.map { "이름 : ${it}" }) // [이름 : john, 이름 : tom, 이름 : jane]

    println(nameList.any { it == "john" }) // true

    println(nameList.all { it.length >= 3 }) // true

    println(nameList.none { it.startsWith("a") }) // true

    println(nameList.first { it.startsWith("j") }) // john, 찾지 못할 경우 Exception
    println(nameList.firstOrNull { it.startsWith("j") }) // john

    println(nameList.last { it.startsWith("j") }) // jane, 찾지 못할 경우 Exception
    println(nameList.lastOrNull { it.startsWith("j") }) // jane

    println(nameList.count()) // 3
    println(nameList.count { it.startsWith("j") }) // 2
}

컬렉션 함수 - 2

data class Person(val name: String, val birthYear: Int)

fun main() {
    val list = listOf(
        Person("john", 1992),
        Person("tom", 1996),
        Person("jane", 1999),
        Person("john", 2003)
    )

    // {1992=Person(name=john, birthYear=1992), 1996=Person(name=tom, birthYear=1996), 1999=Person(name=jane, birthYear=1999), 2003=Person(name=john, birthYear=2003)}
    println(list.associateBy { it.birthYear })

    // {john=[Person(name=john, birthYear=1992), Person(name=john, birthYear=2003)], tom=[Person(name=tom, birthYear=1996)], jane=[Person(name=jane, birthYear=1999)]}
    println(list.groupBy { it.name })

    val (over98, under98) = list.partition { it.birthYear > 1998 }
    println(over98) // [Person(name=jane, birthYear=1999), Person(name=john, birthYear=2003)]
    println(under98) // [Person(name=john, birthYear=1992), Person(name=tom, birthYear=1996)]
}

컬렉션 함수 - 3

fun main() {
    val numbers = listOf(-3, 7, 2, -10, 1)

    println(numbers.flatMap { listOf(it * 10, it + 10) }) // [-30, 7, 70, 17, 20, 12, -100, 0, 10, 11]

    println(numbers.getOrElse(1) { 50 }) // 7
    println(numbers.getOrElse(10) { 50 }) // 50

    val names = listOf("A", "B", "C", "D")

    // 두 컬렉션 중 작은 컬렉션 기준으로 합치기
    println(names zip numbers) // [(A, -3), (B, 7), (C, 2), (D, -10)]
}

lateinit var

class LateInitSample {
    lateinit var text: String

    fun getLateInitText(): String {
        return if (::text.isInitialized) {
            text
        } else {
            "Default"
        }
    }
}

fun main() {
    val a = LateInitSample()

    println(a.getLateInitText()) // Default

    a.text = "AAA"

    println(a.getLateInitText()) // AAA
}

lazy var

fun main() {
    val number: Int by lazy {
        println("init")
        7
    }

    println("start")
    println(number)
    println(number)
}
start
init
7
7
반응형

+ Recent posts