[Kotlin/코틀린] 10. 유용한 기능(일반 자료형 변환, 객체 자료형 변환, 업 캐스팅, 다운 캐스팅, Is, Pair, Triple, Scope Functions)

2023. 12. 19. 22:46스파르타/Kotlin

 

10. 유용한 기능 (자료형 변환)

 

일반 자료형간의 변환

- 숫자 자료형끼리는 to자료형() 메소드를 활용할 수 있다.

- 문자열을 숫자로 변경할 때는 별도의 메소드가 필요하다.

 

 

 

일반 자료형 변환 예시 코드

var num1 = 20
var num2 = 30.2

var num3 = num2.toInt()
var num4 = num1.toDouble()

var strNum5 = "10"
var strNum6 = "10.21"

var num5 = Integer.parseInt(strNum5)
var num6 = strNum6.toDouble()

println("num3: $num3")
println("num4: $num4")
println("num5: $num5")
println("num6: $num6")

 

 

 

 

 

객체 자료형간의 변환

- 객체 자료형간의 변환은 상속 관계에서 가능하다.

업 캐스팅 (자식 클래스를 부모 클래스의 자료형으로 객체 생성)

다운 캐스팅 (부모 클래스를 자식 클래스의 자료형으로 객체 생성)

 

 

 

 

 

객체 자료형 변환 예시 코드 (업 캐스팅)

fun main() {
    println("몇 마리를 생성하시겠습니까?")
    var count = readLine()!!.toInt()
    var birds = mutableListOf<Bird>()

    for(idx in 0..count-1) {
        println("조류의 이름을 입력해주세요")
        var name = readLine()!!

        // as Bird는 생략가능
        birds.add(Sparrow(name) as Bird)
    }
    println("============조류 생성완료============")
    for(bird in birds) {
        bird.fly()
    }
}

open class Bird(name: String) {
    var name: String

    init {
        this.name = name
    }

    fun fly() {
        println("${name}이름의 조류가 날아요~")
    }
}

class Sparrow(name: String): Bird(name) {

}

 

* 강의에서는 주로 업 캐스팅을 활용한다.

 

 

 

객체 자료형 변환 예시 코드 (다운 캐스팅)

fun main() {
    println("몇 마리를 생성하시겠습니까?")
    var count = readLine()!!.toInt()
    var birds = mutableListOf<Bird>()

    for(idx in 0..count-1) {
        println("조류의 이름을 입력해주세요")
        var name = readLine()!!

        birds.add(Sparrow(name) as Bird)
    }
    println("============조류 생성완료============")
    for(bird in birds) {
        bird.fly()
    }
    // 다운캐스팅 오류
    // Sparrow는 Bird가 가져야할 정보를 모두 가지고 있지 않기 때문임
//    var s1:Sparrow = birds.get(0)
}

open class Bird(name: String) {
    var name: String

    init {
        this.name = name
    }

    fun fly() {
        println("${name}이름의 조류가 날아요~")
    }
}

class Sparrow(name: String): Bird(name) {

}

 

 

 

is

코틀린에서는 is 키워드를 활용해서 자료형의 타입을 확인할 수 있다.

 

if(name is String) {
    println("name은 String 타입입니다")
} else {
    println("name은 String 타입이 아닙니다")
}

 

 

 

Pair, Triple

여러 인스턴스를 리턴할 수 있다.

메소드는 기본적으로 하나의 데이터를 리턴한다.

두 개 이상의 데이터를 포함하는 데이터클래스를 설계하고 인스턴스를 리턴하면 가능하다.

하지만 매번 불필요한 클래스를 만드는 행위는 비효율적이다.

코틀린은 데이터를 두 개, 세 개 리턴할 수 있는 클래스를 제공한다.

 

 

 

 

Pair 활용 예시 코드

Pair 를 활용하면 두 개의 값을 리턴할 수 있다.

var chicken = Chicken()
var eggs = chicken.getEggs()
var listEggs = eggs.toList()

//    first, second로 관리
//    var firstEgg = eggs.first
//    var secondEgg = eggs.second

// 리스트로 관리
var firstEgg = listEggs[0]
var secondEgg = listEggs[1]

println("달걀의 종류는 ${eggs} 입니다.")
println("리스트 달걀의 종류는 ${listEggs} 입니다.")
println("첫번째 달걀의 종류는 ${firstEgg} 입니다.")
println("두번째 달걀의 종류는 ${secondEgg} 입니다.")
}

class Chicken {
    fun getEggs(): Pair<String, String> {
        var eggs = Pair("달걀", "맥반석")
        return eggs
    }
}

 

 

 

 

Triple 활용 예시 코드

Triple을 활용하면 세 개의 값을 리턴할 수 있어요

fun main() {
    var chicken = Chicken()
    var eggs = chicken.getThreeEggs()
    var listEggs = eggs.toList()

//    first, second, third로 관리
//    var firstEgg = eggs.first
//    var secondEgg = eggs.second
//    var eggTime = eggs.third

    // 리스트로 관리
    var firstEgg = listEggs[0]
    var secondEgg = listEggs[1]
    var eggTime = listEggs[2]

    println("달걀의 정보는 ${eggs} 입니다.")
    println("리스트 달걀의 정보는 ${listEggs} 입니다.")
    println("첫번째 달걀의 종류는 ${firstEgg} 입니다.")
    println("두번째 달걀의 종류는 ${secondEgg} 입니다.")
    println("달걀은 ${eggTime}에 나왔습니다.")
}

class Chicken {
    fun getTwoEggs(): Pair<String, String> {
        var eggs = Pair("달걀", "맥반석")
        return eggs
    }

    fun getThreeEggs(): Triple<String, String, Int> {
        var eggs = Triple("달걀", "맥반석", 20230101)
        return eggs
    }
}

 

 

 

 

 

 

Scope Functions

Scope Functions 은 주로 코틀린에서 사용되는 용어이다.

자기 자신의 객체를 전달해서 효율적인 처리를 할 수 있다.

객체를 사용할 때 임시로 Scope 를 만들어서 편리한 코드 작성을 도와준다.

주로 let, with, also, apply, run 등이 있다.

 

 

 

 

let function

var strNum = "10"

var result = strNum?.let {
    // 중괄호 안에서는 it으로 활용함
    Integer.parseInt(it)
}

println(result!!+1)

 

중괄호 블록 안에 it으로 자신의 객체를 전달하고 수행한 결과를 반환한다.

 

 

 

 

 

with function

var alphabets = "abcd"

with(alphabets) {
//      var result = this.subSequence(0,2)
    var result = subSequence(0,2)
    println(result)
}

 

중괄호 블록 안에 this 로 자신의 객체를 전달하고 코드를 수행한다.

this는 생략해서 사용할 수 있으므로 반드시 null 이 아닐때만 사용하는 것이 좋다.

 

 

 

 

 

also function

fun main() {
    var student = Student("참새", 10)

    var result = student?.also {
        it.age = 50
    }
    result?.displayInfo()
    student.displayInfo()
}

class Student(name: String, age: Int) {
    var name: String
    var age: Int

    init {
        this.name = name
        this.age = age
    }

    fun displayInfo() {
        println("이름은 ${name} 입니다")
        println("나이는 ${age} 입니다")
    }
}

 

중괄호 블록안에 it으로 자신의 객체를 전달하고 객체를 반환해준다.

apply 와 함께 사용한다.

 

 

 

 

apply function

fun main() {
    var student = Student("참새", 10)

    var result = student?.apply {
        student.age = 50
    }
    result?.displayInfo()
    student.displayInfo()
}

class Student(name: String, age: Int) {
    var name: String
    var age: Int

    init {
        this.name = name
        this.age = age
    }

    fun displayInfo() {
        println("이름은 ${name} 입니다")
        println("나이는 ${age} 입니다")
    }
}

 

중괄호 블록 안에 this로 자신의 객체를 전달하고 객체를 반환해준다.

주로 객체의 상태를 변화시키고 바로 저장하고 싶을 때 사용한다.

 

 

 

 

 

run function (객체에서 호출하지 않은 경우의 예시)


var totalPrice = run {
    var computer = 10000
    var mouse = 5000

    computer+mouse
}
println("총 가격은 ${totalPrice}입니다")

 

 

 

 

 

run function (객체에서 호출하는 경우의 예시)

fun main() {
    var student = Student("참새", 10)
    student?.run {
        displayInfo()
    }
}

class Student(name: String, age: Int) {
    var name: String
    var age: Int

    init {
        this.name = name
        this.age = age
    }

    fun displayInfo() {
        println("이름은 ${name} 입니다")
        println("나이는 ${age} 입니다")
    }
}

 

with 와 달리 null 체크를 수행할 수 있으므로 더욱 안전하게 사용 가능하다.

 

 

 

 

 

* Scope Functions 에는 수신객체와 람다 함수간의 긴밀한 관계가 존재한다.

T는 수신객체를 의미한다.

block: 내부는 람다함수의 소스코드이다.

 

 

Scope Functions은 크게 두 가지로 구분이 가능하다.

1. 명시적으로 수신객체 자체를 람다로 전달하는 방법

2. 수신객체를 람다의 파라미터로 전달하는 방법

 

수신객체는 it으로 사용할 수 있다.

하지만 it은 Scope Function을 다중으로 사용할 때 문제가 발생할 수 있다.

Child Function에서 Shadow가 되어서 제대로 참조하지 못할 수 있어요

그래서 it은 다른 이름으로 변경해서 사용하기도 한다.

// 수신객체 자체를 람다의 수신객체로 전달하는 방법
public inline fun <T, R> T.run(block: T.() -> R): R
public inline fun <T> T.apply(block: T.() -> Unit): T
public inline fun <T, R> with(receiver: T, block: T.() -> R): R

// 수신객체를 람다의 파라미터로 전달
public inline fun <T> T.also(block: (T) -> Unit): T
public inline fun <T, R> T.let(block: (T) -> R): R

// Scope Function을 중첩으로 사용할 경우 누가 누구의 범위인지 알수 없다!
// Implicit parameter 'it' of enclosing lambda is shadowed 경고 발생!

data class Person(
    var name: String = "",
    var age: Int? = null,
    var child: Person? = null
)

// 잘못된 예시
Person().also {
    it.name = "한석봉"
    it.age = 40
    val child = Person().also {
        it.name = "홍길동" // 누구의 it인지 모른다!
        it.age = 10 // 누구의 it인지 모른다!
    }
    it.child = child
}

// 수정한 예시
Person().also {
    it.name = "한석봉"
    it.age = 40
    val child = Person().also { c ->
        c.name = "홍길동"
        c.age = 10
    }
    it.child = child
}

 

 

 

 

<let, also, apply, run, with를 표로 정리>

  Scope에서 접근방식 this Scope에서 접근방식 it
블록 수행 결과 를 반환 run, with let
객체 자신을 반환 apply also