Kotlin/이해하기

[Kotlin] Kotlin 기본 문법 이해하기 -4 : 함수, 파라미터, 스코프 함수

adjh54 2024. 11. 23. 15:36
반응형
해당 글에서는 Kotlin의 기본 문법 중 함수, 파라미터, 스코프 함수에 대해 알아봅니다.

 

 

💡 [참고] Kotlin의 문법들에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.
주제 링크
Kotlin 기본 문법 이해하기 -1 : 주요 특징 및 문법 이해(변수 및 데이터 타입, 캐스팅, Null) https://adjh54.tistory.com/607
Kotlin 기본 문법 이해하기 -2 : Control flow (조건식, RANGE, 반복문) https://adjh54.tistory.com/608
Kotlin 기본 문법 이해하기 -3 : 클래스, 인터페이스, 구현체 https://adjh54.tistory.com/609
Kotlin 기본 문법 이해하기 -4 : 함수, 파라미터, 스코프 함수 https://adjh54.tistory.com/610

 

 

1) Kotlin function


💡 Kotlin function

- Kotlin의 함수(function)는 코드의 재사용성과 모듈화를 위한 핵심 요소입니다.

 

 

1. 함수 선언(Function declaration)


💡 함수 선언(Function declaration)

- Kotlin에서 일반적인 함수는 ‘fun’ 키워드로 선언합니다. 해당 함수에서는 파라미터를 정의하고 리턴 타입을 정의합니다.
요소 설명
fun 함수임을 선언하는 키워드
functionName 함수의 이름
parameter1, parameter2, ... 함수의 매개변수들 (옵션)
Type1, Type2, ... 매개변수들의 타입
ReturnType 함수의 반환 타입 (생략 가능, 생략 시 Unit으로 간주)
return 함수의 결과를 반환하는 키워드 (ReturnType이 Unit이 아닌 경우 필요)
fun functionName(parameter1: Type1, parameter2: Type2, ...): ReturnType {
    // 함수 본문
    return result
}

 

 

💡 함수 선언(Function declaration) 예시

- normalFun 함수 이름을 지정하고, parameter1: String, parameter2: Int 파라미터로 지정하였고, 리턴타입을 String으로 지정하였습니다. 그리고 비즈니스 로직 처리는 함수 본문에서 구현합니다.
/**
 * 일반 함수 구조 : 파라미터, 리턴타입 정의
 */
fun normalFun(parameter1: String, parameter2: Int): String {
    // 함수 본문
    val result = parameter1 + parameter2
    return result
}

 

 

2. 단일 표현식 함수(Single Expression Functions)


💡 단일 표현식 함수(Single Expression Functions)

- 함수 본문이 단일 표현식인 경우, 중괄호와 return 문을 생략할 수 있습니다
/**
 * 단일 함수 구조 
 */
fun singleFun(x: Int): Int = x * 2

 

 

3. 확장 함수(Extension Functions)


💡 확장 함수(Extension Functions)

- 기존 클래스에 새로운 함수를 추가할 수 있습니다.

- 아래의 예와 같이 String 클래스 내에 addExclamation()을 추가합니다.
- 이를 통해서 원본 클래스의 소스 코드를 수정하지 않고 클래스에 새로운 기능을 추가할 수 있습니다.
/**
 * 확장 함수 구조
 */
private fun String.addExclamation() = "$this!"

/**
 * 확장 함수 호출 : String 클래스를 확장하여 addExclamation를 추가하였습니다. 이에 대해 호출을 하여 기존의 클래스에 함수를 추가합니다.
 */
fun extensionFunCall() {
    val str = "Hello"
    println(str.addExclamation())  // 출력: Hello!
}

 

 

[더 알아보기]

💡 확장 함수의 경우는 확장 범위가 클래스 내인가 아니면 전역인가?


- 확장 함수의 범위는 기본적으로 전역(global)입니다. 다시 말해, 확장 함수는 클래스 내부가 아닌 파일의 최상위 레벨에서 정의됩니다.
- 확장 함수는 해당 함수가 정의된 파일 내에서 어디서든 사용할 수 있습니다. 다른 파일에서도 해당 확장 함수를 import 하여 사용할 수 있습니다.

 

4. 고차 함수(Higher-Order Functions)


💡 고차 함수(Higher-Order Functions)

- 함수를 인자로 받거나 함수를 반환하는 함수를 정의할 수 있습니다. 이를 통해서 프로그래밍의 유연성과 재사용성을 크게 향상시킵니다.
/**
 * 고차함수 구조
 */
fun higherOrderFun(x: Int, y: Int, op: (Int, Int) -> Int): Int = op(x, y)

/**
 * 고차함수 호출 : 함수 파라미터
 */
fun higherOrderFunCall() {
    // 덧셈 연산
    val sum = higherOrderFun(5, 3) { a, b -> a + b }
    println(sum)  // 출력: 8

    // 곱셈 연산
    val product = higherOrderFun(5, 3) { a, b -> a * b }
    println(product)  // 출력: 15
}

 

 

5. 인라인 함수(Inline Functions)


 💡 인라인 함수(Inline Functions)

- Kotlin의 인라인 함수는 성능 최적화를 위해 사용되는 특별한 종류의 함수입니다.
특징 설명
선언 방법 'inline' 키워드를 사용하여 선언
컴파일 시 동작 함수 호출 부분에 함수의 본문 코드가 직접 삽입됨
주요 사용 목적 고차 함수에서 람다 표현식의 오버헤드를 줄이기 위해 사용
성능 영향 적절히 사용 시 성능 향상, 과도한 사용 시 코드 크기 증가
적합한 상황 작은 크기의 유틸리티 함수, 반복적으로 호출되는 간단한 연산
주의사항 복잡하거나 큰 함수에는 부적합, 코드 크기 증가 가능성

 

💡 [참고] inline 함수의 주의사항
주의 사항 설명
코드 크기 모든 함수를 인라인으로 만들면 코드 크기가 불필요하게 커질 수 있습니다.
성능 영향 복잡하거나 큰 함수를 인라인으로 만들면 오히려 성능이 저하될 수 있습니다.
사용 권장 적절한 상황에서만 인라인 함수를 사용하는 것이 좋습니다.

 

 

💡 사용예시

-  기존의 고차함수에 인라인 함수로 변경하였습니다.
1. 성능 최적화: 인라인 함수는 컴파일 시 함수 호출 부분에 함수의 본문 코드가 직접 삽입됩니다. 이로 인해 함수 호출에 따른 오버헤드가 줄어들어 성능이 향상될 수 있습니다.
2. 람다 표현식 오버헤드 감소: 특히 고차 함수에서 람다 표현식을 사용할 때 발생할 수 있는 오버헤드를 줄이는 데 효과적입니다.
3. 코드 최적화: 작은 크기의 유틸리티 함수나 반복적으로 호출되는 간단한 연산에 적합합니다.
@Component
class FunctionComponent {
	/**
	 * 인라인 고차함수 구조
	 */
	private inline fun inlineHigherOrderFun(x: Int, y: Int, op: (Int, Int) -> Int): Int = op(x, y)
	
	/**
	 * 인라인 고차함수 호출 : 함수 파라미터
	 */
	fun inlineHigherOrderFunCall() {
	    // 덧셈 연산
	    val sum = higherOrderFun(5, 3) { a, b -> a + b }
	    println(sum)  // 출력: 8
	
	    // 곱셈 연산
	    val product = higherOrderFun(5, 3) { a, b -> a * b }
	    println(product)  // 출력: 15
	}
}

 

 

6. 연산자 함수(operator fun)


 💡 연산자 함수(operator fun)

- Kotlin에서 연산자 오버로딩을 구현하기 위해 사용되는 특별한 함수입니다.

- fun 키워드 앞에 operator 키워드를 사용하여 선언하며 기존 연산자(+, -, *, / 등)의 동작을 사용자 정의 클래스에 맞게 재정의할 수 있습니다.

 

💡 operator fun 연산자의 오버로딩 함수
주요 연산자 함수 설명
plus() + 연산자
minus() - 연산자
times() * 연산자
div() / 연산자
get() [] 인덱스 연산자
inc() ++ 증가 연산자
dec() -- 감소 연산자
class Point(var x: Int, var y: Int) {
    // + 연산자 오버로딩
    operator fun plus(other: Point): Point {
        return Point(x + other.x, y + other.y)
    }
    
    // - 연산자 오버로딩
    operator fun minus(other: Point): Point {
        return Point(x - other.x, y - other.y)
    }
}

fun main() {
    val p1 = Point(10, 20)
    val p2 = Point(30, 40)
    
    // 연산자 사용
    val sum = p1 + p2      // plus() 함수 호출
    val diff = p1 - p2     // minus() 함수 호출
    
    println("sum: (${sum.x}, ${sum.y})")     // 출력: sum: (40, 60)
    println("diff: (${diff.x}, ${diff.y})")   // 출력: diff: (-20, -20)
}
 

Operator overloading | Kotlin

 

kotlinlang.org

 

7. suspend fun


 💡 suspend fun

- Kotlin의 코루틴에서 사용되는 특별한 함수입니다.
- 비동기 작업을 처리하기 위해 사용되며, 코루틴 내부에서만 호출할 수 있습니다.

 

 💡[참고] 해당 작업은 코루틴 메서드를 사용하기에 아래의 라이브러리를 추가해야 합니다.
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core")

 

 

💡 사용예시

- 해당 예시에서는 코루틴을 활용하여 비동기 처리를 하는 메서드를 구성하였습니다.

1. fetchUserData(): suspend 함수로, User 객체를 반환하기 전에 1초간 대기하는 비동기 작업을 수행합니다.
2. main(): runBlocking을 사용하여 코루틴 스코프 내에서 suspend 함수를 호출하는 예시를 보여줍니다.
package com.blog.kotlinspringbootform.component.functionType

import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking

/**
 * Please explain the class!!
 *
 * @fileName      : FunSuspend
 * @author        : jonghoon
 * @since         : 11/23/24
 */
class FunSuspend {
    // suspend 함수 예시
    private suspend fun fetchUserData(): User {
        delay(1000) // 1초 대기
        return User("John", 25)
    }

    // 코루틴 내에서 suspend 함수 사용
    fun main() = runBlocking {
        val user = fetchUserData() // suspend 함수 호출
        println(user)
    }

    data class User(
            val name: String,
            val age: Int
    )
}

 

 

8. 꼬리 재귀함수(tailrec fun)


💡 꼬리 재귀함수(tailrec fun)

- 꼬리 재귀 최적화를 위한 특별 함수를 의미합니다.
- 재귀 호출 함수를 구성하며, 스택 오버플로우를 발생하지 않도록 컴파일러가 최적화를 수행합니다.
package com.blog.kotlinspringbootform.component.functionType

/**
 * Tailrec 함수 사용 예시
 *
 * @fileName      : FunTailrec
 * @author        : jonghoon
 * @since         : 11/23/24
 */
class FunTailrec {
    // 일반적인 재귀 함수
    tailrec fun factorial(n: Long, accumulator: Long = 1): Long {
        return when (n) {
            0L -> accumulator
            else -> factorial(n - 1, n * accumulator)
        }
    }

    fun main() {
        println(factorial(5)) // 출력: 120
    }
}

 

 

Functions | Kotlin

 

kotlinlang.org

 

9. 중위표기 함수(infix fun)


 💡 중위표기 함수(infix fun)

- 중위 표기법을 사용할 수 있게 해주는 특별한 함수입니다.
- 두 개의 값 사이에 함수 이름을 위치시켜 더 자연스러운 문법을 제공합니다.
class MyNumber(val value: Int) {
    infix fun add(other: MyNumber): MyNumber {
        return MyNumber(this.value + other.value)
    }
}

fun main() {
    val num1 = MyNumber(10)
    val num2 = MyNumber(20)
    
    // 일반적인 호출
    val result1 = num1.add(num2)
    
    // 중위 표기법 사용
    val result2 = num1 add num2
    
    println(result2.value) // 출력: 30
}

 

 

[ 더 알아보기 ]

💡 중위 표기법(Infix Notation)


- 일반적인 함수 호출 방식인 obj.function(param) 대신 obj function param 형태로 함수를 호출할 수 있게 해주는 표기법입니다.

- 더 자연스럽고 읽기 쉬운 코드를 작성할 수 있게 해 줍니다.
- 주로 두 객체 간의 연산이나 관계를 표현할 때 사용됩니다.

 

 

반응형

 

 

2) Kotlin Paramerter


 

 

Functions | Kotlin

 

kotlinlang.org

 

1. 기본 매개변수(Paramerter)


 💡 기본 매개변수(Paramerter)

- 함수 내에서 기본적으로 파라미터 타입에 대해서만 지정하며, default 값을 지정한 파라미터를 지정합니다.
 /**
   * 일반적인 타입을 지정한 파라미터
   */
  fun normalParam(name: String): String {
      return "hello $name"
  }

  /**
   * default 값을 가지는 파라미터
   */
  fun defaultParam(name: String = "lee"): String {
      return "hello $name"
  }

 

 

2. 선택적 매개변수(Optional Parameter)


 💡 선택적 매개변수(Optional Parameter)

- 함수 호출 시 생략할 수 있는 매개변수를 의미합니다.
- Kotlin에서는 매개변수에 물음표(?)를 붙여 nullable 타입으로 선언하여 구현합니다.
/**
 * optional 파라미터를 사용한 함수
 */
private fun optionalParam(name: String, age: Int?) {
    if (age != null) {
        println("$name is $age years old")
    } else {
        println("$name's age is unknown")
    }
}

fun optionalParamCall() {
    optionalParam("John", 25)    // age 값 제공
    optionalParam("Jane", null)  // age 값을 null로 제공
}

 

 

3. 가변 인자 (Vararg Parameter)


 💡 가변 인자 (Vararg Parameter)

- 함수 내에 파라미터에 vararg 키워드를 사용하여 가변 개수의 인자를 받는 함수를 정의합니다.


1. 타입: vararg 키워드 뒤에 지정된 타입으로 제한됩니다. 예를 들어, 'vararg numbers: Int'는 정수형 인자만 받을 수 있습니다.
2. 개수: 가변 인자 함수는 0개 이상의 인자를 받을 수 있습니다. 즉, 인자를 전혀 전달하지 않거나, 하나 또는 여러 개의 인자를 전달할 수 있습니다.
/**
 * vararg 파라미터를 이용하여 가변 인자 함수
 */
private fun varargSumParam(vararg numbers: Int): Int = numbers.sum()

/**
 * vararg 파라미터를 이용하여 가변 인자 함수 호출
 */
fun varargParamCall() {
    varargSumParam()                      // 인자 없이 호출 가능
    varargSumParam(1)           // 단일 인자
    varargSumParam(1, 2, 3)     // 여러 인자
    varargSumParam(*intArrayOf(1, 2, 3))  // 배열을 전개 연산자(*)와 함께 사용
}

 

 

 

3) Scope Function


💡 Scope Function

- Kotlin에서 객체의 컨텍스트 내에서 코드 블록을 실행할 수 있게 해주는 특별한 함수들입니다
함수 명 Object reference Return value 설명
let it Lambda result 객체를 이용해 작업을 수행하고 ‘결과를 반환’합니다. null 체크에 유용합니다.
run this Lambda result 객체 ‘초기화’와 반환 값의 ‘계산’을 동시에 수행합니다.
with this Lambda result 객체에 대해 여러 작업을 그룹화합니다. 수신자 객체를 매개변수로 받습니다.
apply this Context object 객체 구성에 사용되며, 객체 자체를 반환합니다.
also it Context object 객체에 대한 ‘추가 작업’을 수행하며, ‘객체 자체를 반환’합니다.

 

 

Scope functions | Kotlin

 

kotlinlang.org

 

[ 더 알아보기 ]

💡 객체의 컨텍스트


- 특정 객체의 범위 또는 환경을 의미합니다.
- 예를 들어서, ‘let’ 함수를 사용할 때, 객체의 컨텍스트 내에서 ‘it’ 키워드를 통해 해당 객체에 접근할 수 있습니다.
- 이를 객체의 컨텍스트 내에서 작업을 수행하는 것을 의미합니다.
fun main() {
    val person = Person("Alice", 30)

    // let을 사용한 예시
    person.let {
        println("Name: ${it.name}, Age: ${it.age}")
        it.age += 1
    }
}

 

 

1. let


💡 let

- 객체를 이용해 ‘작업을 수행하고 결과를 반환’합니다. null 체크에 유용합니다.
- 주로 nullable 객체를 다룰 때 사용되며, 람다 내에서 객체를 'it'으로 참조합니다.

 

💡 let 함수 기본 구조

1. 'object'는 let 함수를 호출하는 객체입니다.
2. 'it'은 람다 내에서 객체를 참조하는 데 사용되는 기본 이름입니다. 원하는 경우 다른 이름으로 변경할 수 있습니다.
3. 람다 블록 내에서 객체를 사용하여 작업을 수행합니다.
4. 람다의 마지막 표현식이 let 함수의 반환값이 됩니다.
val result = object.let { it ->
    // 객체를 사용한 코드 블록
    // 'it'은 객체를 참조
    // 마지막 표현식이 반환값
}

 

💡 let 함수를 이용한 예시

- Person이라는 데이터 클래스를 이용하여 작업을 수행하고 결과를 반환합니다.


1. 객체 내에서 let을 이용하여 변수를 처리하는 예시
- 해당 예시에서는 객체 (Person)이라는 값을 접근하여 멤버 변수인 name, age를 접근합니다. 이 접근한 변수를 변환하고 반환하는 처리를 수행합니다.

2. 객체 내에서 let을 이용하여 변수의 null 처리를 하는 예시

- nullablePerson이라는 변수가 null 인지 아닌지에 대한 체크를 수행하고 각각에 맞는 결과값을 반환하는 예시입니다.

3. 리스트 내에서 문자열 길이에 따라 필터링하여 값을 반환받는 예시
- 리스트를 초기화하고, 리스트를 순회하며 길이가 3 초과인 문자열을 반환하는 함수예시입니다.
/**
 * let 함수를 이용한 예시
 */
fun letScopeFun() {
    val person: Person = Person("홍길동", 30)

    // [사용예시-1] 객체 내에서 let을 이용하여 변수를 처리하는 예시
    person.let {
        println("이름: ${it.name}")
        println("나이: ${it.age}")
        it.age += 1                     // 객체 내 변수의 값을 변환하여 반환합니다.
        println("1년 후 나이: ${it.age}")
    }

    // [사용예시-2] 객체 내에서 let을 이용하여 변수의 null 처리를 하는 예시
    val nullablePerson: Person? = null
    val result = nullablePerson?.let { "${it.name}은(는) ${it.age}살입니다." } ?: "Person 객체가 null입니다."
    println(result)        // null 인 경우 => Person 객체가 null입니다. / null이 아닌 경우 => 홍길동은(는) 30살입니다.

    // [사용예시-3] 리스트 내에서 let 함수를 이용하여 필터링하여 값을 반환받는 예시 : 문자 길이가 3초과값 반환
    val numbers = mutableListOf("one", "two", "three", "four", "five")
    val resultList = numbers.map { it.length }.filter { it > 3 }
    println(resultList)     // "three"(5), "four"(4), "five"(4)의 길이가 선택되어 [4, 5, 5]가 출력

}

 

 

2. run


💡 run

- 객체 ‘초기화’와 반환 값의 ‘계산’을 동시에 수행합니다.

- 객체의 멤버 함수나 속성을 여러 번 호출할 때 유용하며, 'this'를 생략할 수 있습니다.

 

 

💡 run 함수 기본 구조

1. 확장 함수로서의 run
- 객체의 컨텍스트 내에서 코드를 실행하며, 'this'를 사용하여 객체를 참조할 수 있습니다.'this'는 생략 가능하며, 람다의 마지막 표현식이 run 함수의 반환값이 됩니다.

2. 독립 실행 함수로서의 run

- 특정 객체의 컨텍스트 없이 코드 블록을 실행합니다. 마찬가지로 람다의 마지막 표현식이 run 함수의 반환값이 됩니다.

- 두 경우 모두 run 함수는 코드 블록을 실행하고 그 결과를 반환한다는 공통점이 있습니다.
- 주요 차이점은 첫 번째 형태가 특정 객체의 컨텍스트 내에서 작동하는 반면, 두 번째 형태는 독립적인 코드 블록으로 작동한다는 것입니다.
// 1. 확장 함수로서의 run 
object.run {
 // 객체의 컨텍스트 내에서 코드 실행
 // 'this'로 객체 참조 (생략 가능)
 // 마지막 표현식이 반환값
}

// 2. 독립 실행 함수로서의 run
run {
 // 코드 블록 실행
 // 객체 컨텍스트 없음
 // 마지막 표현식이 반환값
}

 

💡 run 함수를 이용한 예시

1. 객체 초기화와 계산을 동시에 수행하는 예시

- Person 객체를 생성하고 초기화하면서 동시에 문자열을 반환합니다.

2. 객체의 멤버 함수를 여러 번 호출하는 예시
-StringBuilder 객체를 사용하여 여러 작업을 수행하고 결과를 반환합니다.
package com.blog.kotlinspringbootform.component

import org.springframework.stereotype.Component

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

/**
 * 범위 함수의 사용예시를 관리하는 컴포넌트
 */
@Component
class ScopeFunctionComponent {

    /**
     * run 함수를 이용한 예시
     */
    fun runScopeFun() {

        // 1. 객체 초기화와 계산을 동시에 수행
        val result = run {
            val person = Person("홍길동", 30)      // 객체 초기화
            person.age += 1                                 // 객체 반환값 계산
            "${person.name}은(는) ${person.age}살입니다."     // run 함수 반환 값
        }
        println(result)  // 출력: 홍길동은(는) 31살입니다.

        // 2. 객체의 멤버 함수를 여러 번 호출
        val stringBuilder = StringBuilder()             // 객체 초기화
        val length = stringBuilder.run {
            append("Hello")                             // 객체 반환값 계산
            append(" ")                                 // 객체 반환값 계산
            append("World")                             // 객체 반환값 계산
            length                                      // run 함수 반환 값
        }
        println(stringBuilder.toString())  // 출력: Hello World
        println("길이: $length")  // 출력: 길이: 11
    }

}

 

 

3. with


💡 with

- 객체에 대해 여러 작업을 그룹화합니다. 수신자 객체를 매개변수로 받습니다.

- 특정 객체에 대해 여러 작업을 수행할 때 사용되며, 람다 내에서 'this'를 생략할 수 있습니다.

 

💡 with 함수 기본 구조

- 'with' 함수는 수신 객체를 첫 번째 인자로 받고, 두 번째 인자로 람다 함수를 받습니다.
- 람다 함수 내에서 수신 객체의 멤버에 직접 접근할 수 있으며, 'this'를 생략할 수 있습니다.
- 람다의 마지막 표현식이 with 함수의 반환값이 됩니다.
val result = with(object) {
    // 객체의 속성이나 함수를 직접 사용
    // 'this'는 생략 가능
    // 마지막 표현식이 반환값
}

 

💡 with 함수를 이용한 예시

1. 객체의 여러 속성을 한 번에 설정하는 예시

- Person 객체의 여러 속성을 설정하고 문자열을 반환합니다.

2. 복잡한 계산을 수행하는 예시
- StringBuilder를 사용하여 문자열을 구성하고 그 결과를 반환합니다.
package com.blog.kotlinspringbootform.component

import org.springframework.stereotype.Component

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

/**
 * 범위 함수의 사용예시를 관리하는 컴포넌트
 */
@Component
class ScopeFunctionComponent {

	fun withScopeFun() {
	    val person = Person("홍길동", 30)
	
	    // 1. 객체의 여러 속성을 한 번에 설정
	    val result = with(person) {
	        name = "김철수"
	        age = 25
	        "이름: $name, 나이: $age"
	    }
	    println(result)  // 출력: 이름: 김철수, 나이: 25
	
	    // 2. 복잡한 계산을 수행
	    val numbers = listOf(1, 2, 3, 4, 5)
	    val sum = with(numbers) {
	        val doubledNumbers = map { it * 2 }
	        doubledNumbers.sum()
	    }
	    println("합계: $sum")  // 출력: 합계: 30
	}
}

 

 

4. apply


 💡 apply

- 객체 구성에 사용되며, 객체 자체를 반환합니다.

- 주로 객체 초기화에 사용되며, 람다의 결과가 아닌 객체 자체를 반환합니다.

 

💡 apply 함수 기본 구조

- 'apply' 함수는 수신 객체 지정 람다의 한 형태입니다.
- 객체를 구성하고 그 객체를 반환합니다. 람다 내에서 수신 객체의 멤버에 직접 접근할 수 있으며, 'this'를 생략할 수 있습니다.
- 람다의 결과와 관계없이 항상 수신 객체 자체를 반환합니다.
val object = SomeClass().apply {
    // 객체의 속성이나 함수를 직접 사용
    // 'this'는 생략 가능
    // 객체 자체가 반환됨
}

 

 

💡 apply 함수를 이용한 예시

1. 객체 초기화 예시
- Person 객체를 생성하고 속성을 설정한 후 그 객체를 반환합니다.

2. 복잡한 객체 구성 예시
- StringBuilder를 사용하여 문자열을 구성하고 그 객체를 반환합니다.
package com.blog.kotlinspringbootform.component

import org.springframework.stereotype.Component

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

/**
 * 범위 함수의 사용예시를 관리하는 컴포넌트
 */
@Component
class ScopeFunctionComponent {

    /**
     * apply 함수를 이용한 예시
     */
    fun applyScopeFun() {
        // 1. 객체 초기화
        val person = Person("홍길동", 30).apply {
            name = "김철수"
            age = 25
        }
        println("이름: ${person.name}, 나이: ${person.age}")  // 출력: 이름: 김철수, 나이: 25

        // 2. 복잡한 객체 구성
        val stringBuilder = StringBuilder().apply {
            append("Hello")
            append(" ")
            append("World")
            insert(0, "Greeting: ")
        }
        println(stringBuilder.toString())  // 출력: Greeting: Hello World
    }

}

 

 

5. also


 💡 also

- 객체에 대한 추가 작업을 수행하며, 객체 자체를 반환합니다.
- 객체의 속성을 변경하지 않고 부가적인 작업을 수행할 때 유용합니다.
- 특히나 디버깅이나 로깅 목적으로 자주 사용됩니다.

 

 

 

 

💡 also 함수 기본 구조

- 'also' 함수는 수신 객체 지정 람다의 한 형태입니다. 객체를 받아 부가적인 작업을 수행하고, 그 객체를 그대로 반환합니다.
- 람다 내에서 수신 객체는 'it'으로 참조됩니다.
- 주로 객체의 상태를 변경하지 않는 부가적인 작업(로깅, 유효성 검사 등)에 사용됩니다.
val object = SomeClass().also {
    // 객체에 대한 부가적인 작업 수행
    // 'it'을 통해 객체 참조
    // 객체 자체가 반환됨
}

 

 

💡 also 함수를 이용한 예시

1. 객체 초기화 후 로깅 예시
- Person 객체를 생성하고 초기화한 후, 객체의 상태를 로그로 출력합니다.

2. 컬렉션 처리 예시

리스트를 생성하고 정렬한 후, 정렬된 상태를 출력합니다.
package com.blog.kotlinspringbootform.component

import org.springframework.stereotype.Component

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

/**
 * 범위 함수의 사용예시를 관리하는 컴포넌트
 */
@Component
class ScopeFunctionComponent {
    /**
     * also 함수를 이용한 예시 
     */
    fun alsoScopeFun() {
        // 1. 객체 초기화 후 로깅
        val person = Person("홍길동", 30).also {
            println("생성된 Person 객체: 이름=${it.name}, 나이=${it.age}")
        }

        // 2. 컬렉션 처리
        val numbers = mutableListOf(3, 1, 4, 1, 5, 9).also {
            it.sort()
            println("정렬된 리스트: $it")
        }

        println("Person 객체: $person")
        println("Numbers: $numbers")
    }

}

 

 

 

4) Kotlin 해당 글 주요 요약


주제 설명 주요 내용
함수 선언 fun 키워드로 선언 - fun: 함수 선언 키워드
- functionName: 함수 이름
- parameter: 매개변수
- ReturnType: 반환 타입
함수 종류 기본 함수 유형 - 단일 표현식 함수
- 기본 매개변수
- 가변 인자 함수
- 지역 함수
- 확장 함수
스코프 함수 객체의 컨텍스트 내에서 코드 실행 - let: null 체크, 지역변수 제한
- run: 객체 구성과 계산
- with: 그룹화된 작업
- apply: 객체 초기화
- also: 부가 작업, 로깅
객체 참조 방식 스코프 함수에서 객체 참조 - this 사용: run, with, apply
- it 사용: let, also
반환값 특성 스코프 함수의 반환 - 람다 결과 반환: let, run, with
- 객체 자신 반환: apply, also
사용 목적 각 함수의 주요 용도 - 객체 초기화
- null 안전성 처리
- 임시 범위 지정
- 객체 조작
- 부가 작업 수행

 

// 1. 기본 함수 선언
fun basicFunction(param1: String, param2: Int): String {
    return "$param1 $param2"
}

// 2. 단일 표현식 함수
fun singleExpression(x: Int, y: Int) = x + y

// 3. 기본 매개변수
fun defaultParams(name: String = "Default", age: Int = 20) = "Name: $name, Age: $age"

// 4. 가변 인자 함수
fun varargFunction(vararg numbers: Int) = numbers.sum()

// 5. 지역 함수
fun outerFunction() {
    fun localFunction() = "Local Function"
    println(localFunction())
}

// 6. 확장 함수
fun String.addPrefix() = "Prefix_$this"

// 7. 스코프 함수 예시들
data class Person(var name: String, var age: Int)

// let 예시 - null 체크
val nullablePerson: Person? = Person("홍길동", 30)
nullablePerson?.let {
    println("${it.name}의 나이는 ${it.age}입니다.")
}

// run 예시 - 객체 구성과 계산
val personInfo = Person("김철수", 25).run {
    age += 1
    "이름: $name, 나이: $age"
}

// with 예시 - 객체 속성 접근
val person = Person("박영희", 28)
with(person) {
    println("$name is $age years old")
}

// apply 예시 - 객체 초기화
val newPerson = Person("이영수", 35).apply {
    name = "새이름"
    age = 40
}

// also 예시 - 로깅과 부가 작업
val numbers = mutableListOf(1, 2, 3)
    .also { println("초기 리스트: $it") }
    .also { it.add(4) }
    .also { println("최종 리스트: $it") }

// 실제 활용 예시
class UserProfile {
    private val userData = mutableMapOf<String, Any>()
    
    fun updateProfile() = userData.apply {
        put("name", "홍길동")
        put("age", 30)
        put("email", "hong@example.com")
    }.also {
        println("프로필 업데이트 완료: $it")
    }.run {
        "프로필 업데이트 성공"
    }
}

 

 

 

 

오늘도 감사합니다. 😀

 

 

반응형