ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Kotlin 문법 강의] 5주차: Kotlin 심화 (Scope Functions/ 확장 함수)
    안드로이드 2024. 3. 12. 15:20

    ✏️ TIL(Today I Learned)

    Kotlin 심화 강의를 들었다. Scope Functions를 배웠는데, 아직 적용해본 코드가 별로 없어서인지 이해가 잘 되지 않았다. 조금 더 공부해보고 따로 정리해야겠다. 또한 확장 함수에 대해서도 배울 때, private 혹은 protected 멤버에 접근하려면 클래스 내부의 멤버함수 형태가 적합하다는 말에 적확히 멤버 함수가 무엇인지 몰라 찾아봤다. 멤버 함수는 해당 클래스에 속하는 함수를 의미한다. 코틀린에서 클래스 내부에 정의된 함수들은 멤버 함수로 간주되며, 클래스의 속성(멤버 변수)과 동일한 범위와 접근 권한을 가지게 된다. 클래스 내부의 모든 private 및 protected 멤버에 직접 접근할 수 있다는 것이다.

     

    📝 공부한 Kotlin 요약 정리

    [유용한 기능]

    자료형 변환)

    • 일반 자료형
      • 다른 숫자 자료형으로 변경:  to자료형()
      • 문자열을 숫자로 변경: Integer.parseInt()
        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") // 출력 결과: num3: 30 (num2의 값을 정수로 변환한 값)
        println("num4: $num4") // 출력 결과: num4: 20.0 (num1의 값을 실수로 변환한 값)
        println("num5: $num5") // 출력 결과: num5: 10 (문자열 "10"을 정수로 변환한 값)
        println("num6: $num6") // 출력 결과: num6: 10.21 (문자열 "10.21"을 실수로 변환한 값)​
    • 객체 자료형
      • 업 캐스팅 (Upcasting): 자식클래스를 부모클래스의 자료형으로 객체 생성
      • 다운 캐스팅 (Downcasting): 부모클래스를 자식클래스의 자료형으로 객체 생성
        fun main() {
            var birds = mutableListOf<Bird>()
            println("조류의 이름을 입력해주세요")
            var name = readLine()!!
            
            //업 캐스팅
            birds.add(Sparrow(name) as Bird) // as Bird는 생략가능
            
            for(bird in birds) {
                bird.fly()
            }
            
            // 다운 캐스팅
        	var s1:Sparrow = birds.get(0) // <-- 다운캐스팅 오류 발생
            // Sparrow는 Bird가 가져야할 정보를 모두 가지고 있지 않기 때문임
        }
        
        open class Bird(name: String) {
            var name: String
            init { this.name = name }
            fun fly() {
                println("${name}이름의 조류가 날아요~")
            }
        }
        
        class Sparrow(name: String): Bird(name) {
        }

     

    자료형의 타입)

    • is 키워드: 자료형의 타입을 확인
      if(name is String) {
          println("name은 String 타입입니다")
      } else {
          println("name은 String 타입이 아닙니다")
      }

     

    여러 인스턴스 리턴)
    메소드는 기본적으로 하나의 데이터 리턴한다. 데이터클래스를 설계하면 여러 인스턴스를 리턴 가능하다. 하지만 불필요한 클래스를 만드는 행위는 비효율적이므로 Pair, Triple 등의 내장 클래스 사용한다.

    • 데이터 클래스
      data class Result(val value1: Int, val value2: String)
      
      fun multipleValues(): Result {
          // 여기에서 여러 값을 계산하거나 가져옴
          return Result(42, "Hello")
      }
    • Pair, Triple 내장 클래스
      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를 만들어서 편리한 코드 작성을 도와준다.

    • Scope Function 종류
      • let:
        수신 객체를 람다 함수의 인자로 전달하고, 람다 함수 내에서 수신 객체를 사용할 수 있도록 한다.
      • run:
        let과 유사하지만, 람다 함수 내에서 수신 객체를 사용하는 대신 람다의 결과를 반환한다.
        with와 달리 null체크를 수행, 더욱 안전 ( safe call(.?)을 붙여 non-null일 때만 실행이 가능 )
      • with:
        수신 객체를 람다 함수의 수신 객체로 전달하고, 람다 함수 내에서는 객체의 멤버에 직접 접근할 수 있다.
        this는 생략해서 사용할 수 있으므로, 반드시 null이 아닐때만 사용 가능
      • apply:
        run과 유사하지만, 항상 수신 객체를 반환하며, 객체를 초기화하고 속성을 설정할 때 자주 사용한다.
        주로 객체의 상태를 변화시키고 바로 저장하고 싶을때 사용
      • also:
        let과 유사하지만, 항상 수신 객체를 반환하며, 부작용을 일으키고 객체 자체를 반환할 때 사용한다.

          Scope에서 접근방식 this Scope에서 접근방식 it
        블록 수행 (람다식) 결과를 반환 run, with let
        객체 자신 (Context Object)을 반환 apply also
        package com.example.mykiosk
        
        class Person(var name: String, var age: Int)
        
        fun main() {
            val person = Person("John", 25)
        
            // 1. let: 객체를 람다 함수의 인자로 전달, 람다의 결과를 반환
            val letResult = person.let {
                it.age += 5
                "Updated age: ${it.age}"
            }
            println("let result: $letResult") // 출력: Updated age: 30
            println("person age: ${person.age}") // person age: 30
        
            // 2. run: 객체를 람다 함수의 수신 객체로 전달, 람다의 결과를 반환
            val runResult = person.run {
                this.age += 3
                "Updated age: ${this.age}"
            }
            println("run result: $runResult") // 출력: Updated age: 33
            println("person age: ${person.age}") // person age: 33
        
            // 3. with: run과 유사하지만, 수신 객체를 람다의 인자로 전달
            val withResult = with(person) {
                this.age += 2
                "Updated age: ${this.age}"
            }
            println("with result: $withResult") // 출력: Updated age: 35
            println("person age: ${person.age}") // person age: 35
        
            // 4. also: 객체를 람다 함수의 수신 객체로 전달, 객체 자체를 반환
            val alsoResult = person.also {
                it.age += 4
            }
            println("also result: $alsoResult") // 출력: Person@1b6d3586
            println("person age: ${person.age}") // person age: 39
        
            // 5. apply: 객체를 람다 함수의 수신 객체로 전달, 객체 자체를 반환
            val applyResult = person.apply {
                this.age += 1
            }
            println("apply result: $applyResult") // 출력: Person@1b6d3586
            println("person age: ${person.age}") // person age: 40
        }

        출처: https://0391kjy.tistory.com/25
    • 수신객체, 람다와의 관계
      • T: 수신객체를 의미
      • block: 내부는 람다함수의 소스코드
      • 수신객체는 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

     

    [확장 함수]

    확장 함수 (extension function): 기존 클래스의 멤버 함수를 추가하거나 변경하는 데 사용되는 특별한 종류의 함수
    확장 함수를 사용하면 기존 클래스의 코드를 변경하지 않고도 새로운 함수를 추가할 수 있다. 확장 함수는 간결하고 모듈화된 코드를 작성하는 데 도움이 되며, 특히 써드파티 라이브러리와의 통합을 쉽게 할 수 있다.

    • 사용
      • 외부에서 클래스의 메소드를 추가
      • 설계한 클래스가 아닐때 외부에서 메소드를 관리
      • 원본 클래스의 일관성을 유지
    •  주의사항
      • 확장함수는 public 멤버에만 접근 O
      • private, protected는 접근 X
      • private 혹은 protected 멤버에 접근하려면 클래스 내부의 멤버함수 형태가 적합
      • 클래스의 멤버함수처럼 상속X
      • 즉, 하위 클래스에서 확장함수를 재정의(오버라이드) X
        // 예시) String 클래스에 확장 함수 추가
        fun String.addExclamation(): String {
            return "$this!"
        }
        
        fun main() {
            val greeting = "Hello"
            val excitedGreeting = greeting.addExclamation()
            
            println(excitedGreeting) // 출력: Hello!
        }

     

     

     

     

     

     

     

     

     

Designed by Tistory.