[Kotlin 기초문법] 총정리

반응형
728x90
반응형

https://github.com/seohaem/kotlin-step1

 

변수선언

1) var 변수 선언

  • 값 변경이 가능하다.
var number1 = 10L

 

2) val 변수 선언

  • 값 변경이 불가능하다.
val number2 = 10L

 

  • 초기화되지 않은 val 변수는 처음 딱 1번만 값 할당이 가능하다.
val number5: Long
number5 = 2
print(number5)

 

3) 타입 선언

  • Long 타입 변수를 선언해보자.
var number3: Long = 10L

 

  • 선언과 동시에 초기화하는 경우에는 타입 선언은 생략 가능하다.
var number3 = 10L

 

  • 초기화하지 않은 선언은 타입을 넣어줘야한다.
var number4: Long
number4 = 1
print(number4)

 

4) Primitive Type

  • 코틀린은 null을 허용하지 않는 Int, Long 등의 자료형을 Primitive Type으로 자동 변환하여 JVM에서 실행된다.
  • null을 허용하는 Int?, Long? 등의 자료형은 Primitive Type으로 자동 변환하지 않고 Heap 공간에서 관리한다.
var number1: Long = 10L // 상황에 따라서 Long -> long으로 변환해준다.
var number2: Long? = 10L

 

5) Null 가능

  • 코틀린에서 모든 변수는 null이 들어갈 수 없다. null 허용은 타입?(Int?, Long? 등)를 사용해야한다.
var number2 = 10L
//    number2 = null // 기본적으로 모든 변수는 null이 들어갈 수 없게끔 설계되었다.

var number3: Long? = 10L
number3 = null // Long? 타입?을 사용해야 null 이 들어갈 수 있다.

 

6) 인스턴스 생성

  • 코틀린은 JAVA와 다르게, 객체 인스턴스화때 new 키워드를 사용하지 않는다.
var person = Person("최태현")

 

 

null 변수

1) null이 아닌 경우 수행

  • str 변수가 null이 아닌 경우 length 함수를 호출한다.
  • str 변수가 null이라 length 함수가 호출되지 않았을 경우, 결과는 null을 반환한다.
str = null
print(str?.length) // 결과는 null

 

2) Elvis 연산자 (null 일 경우 결과값 default 설정)

str?.length 의 결과가 null이면 전체 식의 결과가 0이다.

print(str?.length ?: 0)

 

3) !! : null 이 아님을 명시

fun startsWith(str: String?): Boolean {
    return str!!.startsWith("A")
}

 

  • 만약 null인 경우 컴파일 에러가 발생한다.
println(startsWith("AAA"))
println(startsWith(null)) // 컴파일 에러 발생

 

 

Type

1) 값으로 타입을 추론한다.

val number1 = 3 // Int
val number2 = 3L // Long
val number3 = 3.0f // Float
val number4 = 3.2 // Double

 

2) 명시적 선언으로 타입 변환할 수 있다.

  • toLong(), toString() 등과 같은 toXX() 메서드를 사용해야한다.
//    val number6 :Long = number5 // Type mismatch
val number6 = 3
val number7: Long = number6.toLong() // 명시적 선언

 

3) is 키워드

  • obj가 Person 타입인가? (JAVA에서 instanceof)
fun printAgeIfPerson(obj: Any) {
    if (obj is Person) { // instanceof -> is
        val person = obj as Person // (Person) obj (생략도 가능)
        println(person.name)
        println(obj.name) // smart cast
    }
}

 

  • is 부정
if (!(obj is Person)) { // instanceof not -> is
    println(obj)
}

// 위와 결과 동일
if (obj !is Person) { // instanceof not -> is
    println(obj)
}

 

4) as 키워드

  • 변수 obj가 null 이 아닌 경우 Person을 반환하고 null이면 null을 반환한다.
val person = obj as? Person

 

  • person이 null인 경우 결과는 null이다.
println(person?.name)

 

5) Any 타입

  • Java의 Object 역할 (모든 객체의 최상위 타입)
  • 모든 Primitive Type의 최상위 타입도 Any이다.
  • Any 자체로는 null 을 포함할 수 없다. null로 표현하려면 Any?로 표현
  • Any에 equals, hashCode, toString도 존재한다.

6) Unit

  • Java의 void와 동일한 역할
  • void와 다르게 Unit은 그 자체로 타입 인자로 사용 가능하다.
  • 함수형 프로그래밍에서 Unit은 단 하나의 인스턴스만 갖는 타입을 의미한다.
  • 즉, 코틀린의 Unit은 실제 존재하는 타입이라는 것을 표현한다.

 

7) Nothing

  • 함수가 정상적으로 끝나지 않았다는 사실을 표현하는 역할이다.
  • 무조건 예외를 반환하는 함수나 무한루프 함수 등에 사용한다.

 

 

String

1) String 변수 출력 방법

val person = Person("KIM")
val age = 22

println("이름 : ${person.name}")
println("나이 : $age")

 

2) """ 키워드

val str = """
    ABCDE
    EFG
    AJWJKQLWK
""".trimIndent()

 

3) String에서 특정 인덱스에 해당하는 문자열 가져오기

val str2 = "ABC"
print(str2[0])
print(str2[1])

 

결과
ABCDE
EFG
AJWJKQLWKAB

 

 

연산자

1) 코틀린은 비교 연산자(>, <, >=, <=)를 사용해서 compareTo() 함수를 자동 호출한다.

val money1 = JavaMoney(2000L)
val money2 = JavaMoney(1000L)

if (money1 > money2) { // compareTo() 메서드를 자동으로 호출해준다.
    println("Money1이 Money2보다 금액이 큽니다.")
}

 

2) 코틀린은 == 를 사용해서 equals()를 자동 호출한다.

val money3 = JavaMoney(1000L)
val money4 = money3; // 주소 같음
val money5 = JavaMoney(1000L)

println(money3 == money4) // equals() true
println(money3 == money5) // equals() true

 

3) 주소가 같은지는 === 를 사용한다.

val money3 = JavaMoney(1000L)
val money4 = money3; // 주소 같음
val money5 = JavaMoney(1000L)

println(money3 === money4) // true
println(money3 === money5) // false

 

4) 범위

연산자 설명
in 컬렉션이나 범위에 포함되어있다.
!in 컬렉션이나 범위에 포함되어있지 않다.
a..b a부터 b까지의 범위 객체를 생성한다.
a[i] a에서 특정 index i로 값을 가져온다.
a[i] = b a의 특정 index i에 b를 넣는다.

 

5) operator : 코틀린의 연산자를 직접 정의하기 

연산자 함수
a + b a.plus(b)
a - b a.minus(b)
a * b a.times(b)
a / b a.div(b)
a % b a.rem(b)
a += b a.plusAssign(b)
a -= b a.minusAssign(b)
a *= b a.timesAssign(b)
a /= b a.divAssign(b)
a %= b a.remAssign(b)

 

  • operator 키워드를 사용하여 메서드를 재정의한다.
data class Money(
    val amount: Long
) {
    operator fun plus(other: Money): Money {
        return Money(this.amount + other.amount)
    }
}

 

main()
fun main() {
    val money1 = Money(1000L)
    val money2 = Money(2000L)

    println(money1.plus(money2)) // 가능하긴함
    println(money1 + money2) // + 연산자로 직접 정의 가능
}

 

 

제어문 if

1) if

  • score 가 0과 100 사이에 포함되어있지 않을 경우
if (score !in 0..100) {
    throw IllegalArgumentException("score의 범위는 0부터 100입니다.")
}

 

2) if~else 에서 즉시 return문

fun getPassOrFail(score: Int): String {
    return if (score >= 50) {
        return "P"
    } else {
        return "F"
    }
}

 

 

제어문 when

  • when 값에 따라서 해당하는 값을 리턴한다.
fun getGradeWithSwitch(score: Int): String {
    return when (score / 10) {
        in 90..99 -> "A" // 어떠한 expression이라도 들어갈 수 있다.
        8 -> "B"
        7 -> "C"
        else -> "D"
    }
}
fun judgeNumber(number: Int) {
    when (number) {
        1, 0, -1 -> println("어디서 많이 본 숫자입니다")
        else -> println("1, 0, -1이 아닙니다")
    }
}
fun judgeNumber2(number: Int) {
    when {
        number == 0 -> println("주어진 숫잔는 0입니다.")
        number % 2 == 0 -> println("주어진 숫자는 짝수입니다.")
        else -> println("주어지는 숫자는 홀수입니다.")
    }
}

 

 

반복문

1) 리스트 numbers 반복하여 값 출력

fun main() {
    val numbers = listOf(1L, 2L, 3L)
    for (number in numbers) {
        println(number)
    }
}

 

2) 1부터 3까지 반복

for (i in 1..3) {
    println(i)
}

 

3) 3부터 1까지 -1씩 하며 반복

// 3부터 1까지 내려간다.
for (i in 3 downTo 1) {
    println(i)
}

 

4) 1부터 5까지 +2씩 하며 반복

// 2칸씩 올라간다.
for (i in 1..5 step 2) {
    println(i)
}

 

5) while문 사용 

fun main() {
    var i = 1
    while (i <= 3) {
        println(i)
        i++
    }
}

 

 

예외처리

1) try~catch~finally문

try {
    return str.toInt()
} catch (e: NumberFormatException) {
    throw IllegalArgumentException("주어진 ${str}는 숫자가 아닙니다.")
}

 

  • 코틀린에서는 try~catch문을 return하여 처리 가능하다.
fun parseIntOrThrowV2(str2: String): Int? {
    return try {
        return str2.toInt()
    } catch (e: NumberFormatException) {
        null
    }
}

 

2) CheckedException

Kotlin 에서는 CatchException, UncheckedExcpetion 을 구분하지 않는다.
모두 UncheckedException이기 때문에, 에러를 던지지 않아도 된다.

 

3) use 키워드

  • JAVA의 try~catch~resource 구문을 대체한다.
fun readFile(path: String) {
    // try~catch~resource 구문 자체는 사라지고 use를 사용한다.
    BufferedReader(FileReader(path)).use {
        reader -> println(reader.readLine())
    }
}

 

 

함수 선언

1) 기본 함수 선언 방법

  • 기본적으로 public 함수다. (public 접근제어자 생략이 가능하다.)
  • fun : 함수를 의미하는 키워드
  • max : 함수 이름
  • a, b : 함수의 매개변수
  • Int {...} : 함수의 반환 타입
  • block {} 을 사용하는 경우, 반환 타입이 Unit 이 아닌 경우에는 반환 타입을 명시해줘야한다.
fun max(a: Int, b: Int): Int {
    // 하나의 expression
    return if (a > b) {
        return a
    } else {
        return b
    }
}

 

2) 코틀린의 함수는 하나의 expression으로 표현 가능하다.

fun max2(a: Int, b: Int): Int =
    // 하나의 expression
    if (a > b) {
        a
    } else {
        b
    }

 

3) block {} 생략이 가능하다.

  • 반환 타입 생략 가능 : 하나의 expression으로 선언된 함수(=인 경우)는 반환 타입 추론이 가능하다.
fun max3(a: Int, b: Int) = if (a > b) a else b

 

 

함수 파라미터(parameter)

1) default 값 설정이 가능하다.

fun repeat(
    str: String,
    num: Int = 3, // default 3
    useNewLine: Boolean = true // default true
) { ... }

 

main() - 호출문 예제
repeat("Hello World")
repeat("Hello World", 5, false)
repeat("Hello World", 7)
repeat("Hello World", 1, true)

 

2) 함수 호출시, 매개변수 이름을 명시해줄 수 있다.

repeat2("Hello World", useNewLine = true)

 

num은 default 값인 3이 셋팅된다.

fun repeat2(
    str: String,
    num: Int = 3, // default 3
    useNewLine: Boolean = true // default true
) { ... }

 

3) 매개변수에 이름을 명시해서 budiler와 비슷하게 사용 가능하다.

fun main() {
    // 이름 명시하여 builder 비슷하게 사용 가능
    printNameAndGender2(name = "KIM", gender = "F")
}

fun printNameAndGender2(name: String, gender: String) {
    println(name)
    println(gender)
}

 

4) vararg 키워드 : 가변인자

fun printAll(vararg strArr: String) { // JAVA의 ...을 쓰 는대신 vararg
    for (str in strArr) {
        println(str)
    }
}

 

  • , 로 구분하여 호출
printAll("A", "B", "C") // ,로 구분

 

  • 변수 array 를 * 사용하여 호출
val array = arrayOf("A", "B", "C") // 배열
printAll(*array) // 가변인자 넣어줄때 배열을 바로 넣는 대신, *를 사용해야한다.

 

 

클래스

1) 기본 클래스 선언 

class Person constructor(name: String, age: Int) {
    // Kotlin은 필드를 만들면 getter, setter을 자동으로 만들어준다.
}

 

2) constructor 키워드 생략 가능

  • Kotlin은 기본 생성자를 자동으로 생성해준다. constructor 키워드 생략이 가능하다.
class Person (name: String, age: Int) { // 기본 생성자 작성 (constructor 키워드는 생략 가능)
    // Kotlin은 필드를 만들면 getter, setter을 자동으로 만들어준다.

    // 불변 필드
    val name = name
    // 변경가능 필드
    var age = age
}

 

  • 위 코드를 아래와같이, 생성자로 옮길 수 있다.
class Person2 (val name: String, var age: Int) { // 생성자 작성 (constructor 키워드는 생략 가능)
    // Kotlin은 필드를 만들면 getter, setter을 자동으로 만들어준다.
}

 

3) 필드만 존재하는 경우 중괄호 {} 생략이 가능하다.

class Person3 (
    val name: String,
    var age: Int)

 

 

getter/setter 사용 방법

fun main() {
    // JAVA 클래스를 코틀린에서 사용할때도 아래와 같은 방법으로 사용할 수 있다.
    val person = Person("KIM", 20)
    println(person.name) // getter
    person.age = 10 // setter
}

 

 

생성자

1) 기본 생성자(주 생성자)는 반드시 존재해야한다.

class Person5 (
    // 기본생성자 (=주생성자)는 반드시 존재해야한다.
    val name: String,
    var age: Int) { ... }

 

2) init {...}를 사용하여 초기화 블록 사용이 가능하다.

  • 생성자가 호출되는 시점에 수행된다.
init {
    if (age <= 0) {
        throw IllegalArgumentException("나이는 ${age}일 수 없습니다.")
    }

    println("초기화 블록")
}

 

3) 부 생성자 생성

constructor(name: String): this(name, 1) { // 위의 기본 생성자를 호출
    println("첫번째 부 생성자") // body add 가능
}

 

 

프로퍼티

class Person (
    name: String = "KIM",
    var age: Int = 10) {
    init {
        if (age <= 0) {
            throw IllegalArgumentException("나이는 ${age}일 수 없습니다.")
        }

        println("초기화 블록")
    }

    val name = name // 주 생성자에서 받은 name을 담는다.
        get() = field.uppercase() // field 키워드
    
    ...
}

1) field.uppercase()

field 키워드 사용 이유
  • field 키워드 : 자신을 가리킨다.
  • name.uppercase()로 한다면, name.getter 을 계속 호출하기 때문에 field 키워드를 사용해야한다.
  • ex) name -> get() -> name -> get()

 

2) 프로퍼티 get() 선언시 return문 사용도 가능하다.

val isAdult: Boolean
    get() = this.age >= 20

val isAdult2: Boolean
    get() {
        return this.age >= 20
    }

 

3) set() 선언도 가능하다.

var name = name
    set(value) { // setter
        field = value.uppercase()
    }

 

 

상속

  • 코틀린은 클래스와 메서드는 기본적으로 final 이라 상속이 불가하다. 
  • 인터페이스와 abstract 클래스에서는 final 이 없는 메서드나 프로퍼티 기본적으로 open 이다.
키워드 설명
final override 할 수 없게 한다.
default이다.
open override를 열어준다.
abstract 반드시 override 해야한다.
override 상위 타입을 오버라이드 하고있다.

 

1) 상위 클래스

  • open 키워드를 선언해야만 자식클래스에서 상속을 받을 수 있다.
  • 상속을 받을 경우 자식 클래스는 부모 클래스의 프로퍼티와 함수를 사용할 수 있다.
open class Base(
    // 상위 클래스를 설계할때 생성자 또는 초기화 블록에 사용되는 프로퍼티에는 open을 피해야한다.
    open val number: Int = 100
) {
    init {
        println("Base Class")
        
        // 0 출력 - 상위클래스 생성자가 생성되는 동안, 하위 클래스의 number 는 아직 초기화가 안되었다.
        println(number)
    }
}

 

2) 하위 클래스

  • 하위 클래스의 객체를 생성할대 상위 클래스의 생성자가 호출된다. 따라서 하위 클래스에서 상위 클래스의 메서드나 프로퍼티를 사용할 수 있다.
class Derived(
    override val number: Int
) : Base(number) {
    init {
        println("Derived Class")
    }
}

 

main()
fun main() {
    /*
    Base Class
    0  --> 상위클래스 생성자가 생성되는 동안, 하위 클래스의 number 는 아직 초기화가 안되었다.
    Derived Class
     */
    Derived()
}

 

 

접근제어

  • 생성자 : constructor 키워드 생략이 가능했는데, 접근지시어를 적으려면 constructor 키워드를 명시해야한다.
  • 프로퍼티 : getter, setter 에 동일하게 적용된다.
  public protected internal private
기본 모든 곳에서 접근 가능 선언된 클래스 또는 하위 클래스에서만 접근 가능 같은 모듈에서만 접근 가능 선언된 클래스 내에서만 접근 가능
파일 모든 곳에서 접근 가능 파일 최상단에 사용 불가능 같은 모듈에서만 접근 가능 같은 파일 내에서만 접근 가능
클래스 모든 곳에서 접근 가능 선언된 클래스 또는 하위 클래스에서만 접근 가능 같은 모듈에서만 접근 가능 선언된 클래스 내에서만 접근 가능
생성자 모든 곳에서 접근 가능 선언된 클래스 또는 하위 클래스에서만 접근 가능 같은 모듈에서만 접근 가능 선언된 클래스 내에서만 접근 가능
프로퍼티 모든 곳에서 접근 가능 선언된 클래스 또는 하위 클래스에서만 접근 가능 같은 모듈에서만 접근 가능 선언된 클래스 내에서만 접근 가능

 

1) 프로퍼티 제어

  • setter에만 추가로 가시성 부여 가능하다.
class Car(
    internal val name: String, // getter, setter 부여
    private val owner: String, // getter, setter 부여
    _price: Int
) {
    // default public
    var price = _price

    // private set, public get
    var price2 = _price
        private set
}

 

 

object 키워드

1) companion object

  • 해당 블록 안에는 모두 static으로 선언된다. 
  • const : 컴파일시에 변수가 할당된다. 상수에 사용하기 위한 용도로, 기본타입과 String에 붙일 수 있다.
  • 코틀린에서는 static 키워드가 없고 companion object 를 사용한다.
class Person private constructor(
    var name: String,
    var age: Int
) {
    // static : 클래스가 인스턴스화 될때 새로운 값이 복제되는것이 아니라 정저긍로 인스턴스끼리의 값을 공유한다.
    // companion objedct : 클래스와 동행하는 유일한 오브젝트 (동반객체도 하나의 객체로 간주된다. 이름을 붙일 수 있고, interface를 구현할수도 있다.)
    // kotlin 에는 static이 없다. companion object 라고 해야한다.
    companion object Factory : Log { // 해당 블록 안에는 모두 static 으로 선언된다.
        // const : 컴파일시에 변수가 할당된다. (상수에 붙이기 위한 용도, 기본 타입과 String에 붙일 수 있음)
        // 기본 val : 런타임시에 변수가 할당된다.
        const val MIN_AGE = 1

        fun newBaby(name: String): Person {
            return Person(name, MIN_AGE)
        }

        override fun log() {
            println("나는 Person 클래스의 동행 객체입니다.")
        }

        // 유틸성 함수들을 넣어도 되지만, 최상단 파일을 활용하는 것을 추천한다.
    }
}

 

2) object 키워드

  • object : 단 하나의 인스턴스만 갖는 싱글톤 클래스를 생성한다.
fun main() {
    // 유일한 객체이므로 바로 접근 가능하다.
    Singleton.a
    Test.a
}

object Singleton {
    var a: Int = 0
}

object Test {
    var a: Int = 0
}

 

3) 익명클래스 : object : Class명

fun main() {
    moveSomething(object : Movable { // object : 타입이름 으로 익명클래스 표현
        override fun move() {
            println("움직인다")
        }

        override fun fly() {
            println("난다")
        }

    })
}

 

 

중첩 클래스

1) 클래스 안의 클래스 2) 클래스 안의 inner 클래스
바깥 클래스 참조가 없다. (권장) 바깥 클래스 참조가 존재한다. 
이 경우, 참조를 해지하지 못하게되어 메모리 누수가 생길 수 있다.

1) 클래스 안의 클래스

class JavaHouse(
    private val adress: String,
    private val livingRoom: LivingRoom
) {
    class LivingRoom( // 기본적으로 바깥 클래스에 대한 연결이 없는 중첩 클래스
        private var area: Double
    )
}

 

2) 클래스 안의 inner 클래스

  • inner : 바깥 클래스에 대한 참조가 존재하는 중첩클래스
class JavaHouse2(
    private val address: String,
    private val livingRoom: LivingRoom
) {
    inner class LivingRoom( // 기본적으로 바깥 클래스에 대한 연결이 없는 중첩 클래스
        private var area: Double
    ) {
        val address: String
            get() = this@JavaHouse2.address
    }
}

 

 

다양한 클래스

1) data class : equals, hashCode, toString 자동으로 만들어준다.

data class PersonDto ( // data 키워드 : equals, hashCode, toString 자동으로 만들어준다.
    // name arguments 사용하면 builder 와 같은 효과도 얻을 수 있다.
    val name:String,
    val age: Int
    ) {

}

 

2) enum class

enum class Country(
    private val code: String
) {
    KOREA("KO"),
    AMERICA("US")
    ;
}

 

3) sealed class

  • 컴파일러는 부모 클래스를 상속받은 자식 클래스들의 존재를 알지 못하는데, sealed class를 사용하면 자식 클래스의 존재를 알게되어 제한하는 특성을 가진다.
sealed class HyundaiCar(
    val name: String,
    val price: Long
)

// 추상화가 필요한 Entity or DTO에 sealed Class 를 활용한다.
private fun handleCar(car: HyundaiCar) {
    when (car) {
        is Avante -> TODO()
        is Grandeur -> TODO()
        is Sonata -> TODO()
        // else 를 사용하지 않아도 된다. 컴파일러가 자식클래스를 알고있기 때문이다.
    }
}

 

 

배열

1) 배열 생성

val array = arrayOf(100, 200) // 배열 생성

 

2) 배열 index for문

// index
for (i in array.indices) {
    println("${i} ${array[i]}")
}

 

3) 배열 index, value

// index, value
for ((idx, value) in array.withIndex()) {
    println("$idx $value")
}

 

4) 배열에 새로운 원소 추가 : push

array.plus(300)  // 배열에 새로운 element 추가

 

 

Collection

Collection 설명
가변 컬렉션 (Mutable) 컬렉션에 element를 추가, 삭제할 수 있다.
불변 컬렉션 컬렉션에 element를 추가, 삭제할 수 없다.
불변리스트의 1번째 원소에 접근해서 그 안의 필드(예) price)를 바꿀 수는 있다.

 

▶ Null 처리

null 처리 설명
List<Int?> 리스트에 null이 들어갈 수 있다.
리스트 자체는 절대 null이 아니다.
List<Int>? 리스트에 null이 들어갈 수 없다.
리스트 자체는 null일 수 있다.
List<Int?>? 리스트에 null이 들어갈 수도 있다.
리스트 자체도 null일 수 있다.

 

 

List

1) 불변리스트 생성 : listOf()

val numbers = listOf(100, 200) // 불변 리스트

 

2) 빈 리스트 생성 : emptyList<Type>()

  • 타입을 명시해야한다. 만약 타입을 추론할 수 있는 경우에는 타입 생략 가능하다.
val emptyList = emptyList<Int>() // 빈 리스트는 타입을 명시해야한다.

 

3) 0번째 index 원소 출력

println(numbers[0])

 

4) 리스트 for~in

for (number in numbers) {
    println(number)
}

 

5) 리스트 index, value

for ((idx, value) in numbers.withIndex()) {
    println("$idx $value")
}

 

6) 가변 리스트 생성 : mutableListOf()

val numbers2 = mutableListOf(100, 200)
numbers2.add(300)

 

 

Set

1) 가변 Set 생성 : mutableSetOf()

val numbers = mutableSetOf(100L)

 

 

Map

1) 가변 Map 생성 : mutableMapOf()

val oldMap = mutableMapOf<Int, String>()

 

2) index 번째의 원소 값 변경

//    oldMap.put(1, "A")
    oldMap[1] = "MONDAY"
    oldMap[2] = "TUESDAY"

 

3) to : key to value

// 중위 호출 (Pair 클래스를 만들어주고 전달)
mapOf(1 to "MONDAY", 2 to "TUESDAY")

 

4) for문 - keys

for (key in oldMap.keys) {
    println(key)
    println(oldMap.get(key))
    println(oldMap[key])
}

 

5) for문 - entries

for ((key, value) in oldMap.entries) {
    println(key)
    println(value)
}

 

 

확장함수

1) 확장함수 lastChar() : String 클래스를 확장한다는 의미다.

  • fun 확장하려는 클래스.함수이름(파라미터) : 리턴타입 { ... }
  • this를 통해서 불려진 인스턴스 접근이 가능하다.
    • this : 수신객체
    • 확장하려는 클래스 : 수신객체 타입
fun String.lastChar(): Char {
    return this[this.length - 1] // 마지막 문자
}

 

 

infix (중위 함수)

infix fun Int.add2(other: Int): Int {
    return this + other
}

 

main()
fun main() {
    3.add(4)
    3.add2(4)
    3 add2 4 // 중위 함수(infix) 사용
}

 

 

inline 함수

  • 인라인 함수로 정의된 함수는 컴파일 단계에서 호출하는 방식이 아닌, 코드 자체가 복사되는 방식으로 컴파일된다.
  • 함수를 호출한 지점에 함수 본문을 그대로 복붙하고 싶은 경우 사용한다.
    • 함수를 파라미터로 전달할때의 오버헤드를 줄일 수 있다.
    • 인라인 함수의 사용은 성능 측정과 함께 신중하게 사용되어야한다.
inline fun Int.add3(other: Int): Int {
    return this + other
}

 

main()
  • add3() 함수에 바이트 코드가 메모리에 할당되어있고, main()의 내용은 sum() 함수의 바이트 코드가 저장된 주소를 호출하는 바이트 코드를 포함하는데, inline 함수이기 때문에 직접 a + b로 복붙한다.
fun main() {
    3.add3(4) // int var10000 = $this$add$iv + other$iv
}

 

 

지역함수

fun createPerson(firstName: String, lastName: String): Person {
    // depth가 깊어지고 코드가 간결하지는 않다.
    fun validateName(name: String, fieldName: String) {
        if (name.isEmpty()) {
            TODO()
        }
    }

    validateName(firstName, "firstName")
    validateName(lastName, "lastName")

    return Person(firstName, lastName, 1)
}

 

 

람다

  • 코틀린에서는 람다가 시작하는 지점에 참조하고있는 변수들을 모두 포획해서 정보를 가지고있기 때문에, final인 변수만 사용 가능한 JAVA와 다르게 아무런 제약이 없다.
  • 함수가 불려지는 시점에 존재하는 변수들을 모두 포획한다. (Closure)
private fun filterFruits(fruits: List<Fruit>, filter: (Fruit) -> Boolean): List<Fruit> {
    val results = mutableListOf<Fruit>()

    for (fruit in fruits) {
        if (filter(fruit)) {
            results.add(fruit)
        }
    }

    return results
}

 

main()
fun main() {
    var a = "aa"
    a = "bb"

    val fruits = listOf(
        Fruit("사과", 1000),
        Fruit("사과", 2000),
        Fruit("사과", 3000)
    )

    filterFruits(fruits) { it.name == a }
}

 

1) use

  • Closeable 구현체에 대한 확장함수
Closeable.kt
@InlineOnly
@RequireKotlin("1.2", versionKind = RequireKotlinVersionKind.COMPILER_VERSION, message = "Requires newer compiler version to be inlined correctly.")
public inline fun <T : Closeable?, R> T.use(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    var exception: Throwable? = null
    try {
        return block(this)
    } catch (e: Throwable) {
        exception = e
        throw e
    } finally {
        when {
            apiVersionIsAtLeast(1, 1, 0) -> this.closeFinally(exception)
            this == null -> {}
            exception == null -> close()
            else ->
                try {
                    close()
                } catch (closeException: Throwable) {
                    // cause.addSuppressed(closeException) // ignored here
                }
        }
    }
}

 

.use { ... } 사용 예제
  • 인라인 함수
  • 람다를 받게 만들어진 함수
class FilePrinter2 {
    fun readFile(path: String) {
        // try~catch~resource 구문 자체는 사라지고 use를 사용한다.
        BufferedReader(FileReader(path)).use { // lambda
                reader -> println(reader.readLine())
        }
    }
}

 

 

반응형

Designed by JB FACTORY