- Kotlin에서 일반적인 함수는 ‘fun’ 키워드로 선언합니다. 해당 함수에서는 파라미터를 정의하고 리턴 타입을 정의합니다.
요소
설명
fun
함수임을 선언하는 키워드
functionName
함수의 이름
parameter1, parameter2, ...
함수의 매개변수들 (옵션)
Type1, Type2, ...
매개변수들의 타입
ReturnType
함수의 반환 타입 (생략 가능, 생략 시 Unit으로 간주)
return
함수의 결과를 반환하는 키워드 (ReturnType이 Unit이 아닌 경우 필요)
funfunctionName(parameter1: Type1, parameter2: Type2, ...): ReturnType {
// 함수 본문return result
}
💡 함수 선언(Function declaration) 예시
- normalFun 함수 이름을 지정하고, parameter1: String, parameter2: Int 파라미터로 지정하였고, 리턴타입을 String으로 지정하였습니다. 그리고 비즈니스 로직 처리는 함수 본문에서 구현합니다.
/**
* 일반 함수 구조 : 파라미터, 리턴타입 정의
*/funnormalFun(parameter1: String, parameter2: Int): String {
// 함수 본문val result = parameter1 + parameter2
return result
}
- 아래의 예와 같이 String 클래스 내에 addExclamation()을 추가합니다. - 이를 통해서 원본 클래스의 소스 코드를 수정하지 않고 클래스에 새로운 기능을 추가할 수 있습니다.
/**
* 확장 함수 구조
*/privatefun String.addExclamation() = "$this!"/**
* 확장 함수 호출 : String 클래스를 확장하여 addExclamation를 추가하였습니다. 이에 대해 호출을 하여 기존의 클래스에 함수를 추가합니다.
*/funextensionFunCall() {
val str = "Hello"
println(str.addExclamation()) // 출력: Hello!
}
[더 알아보기] 💡 확장 함수의 경우는 확장 범위가 클래스 내인가 아니면 전역인가?
- 확장 함수의 범위는 기본적으로 전역(global)입니다. 다시 말해, 확장 함수는 클래스 내부가 아닌 파일의 최상위 레벨에서 정의됩니다. - 확장 함수는 해당 함수가 정의된 파일 내에서 어디서든 사용할 수 있습니다. 다른 파일에서도 해당 확장 함수를 import 하여 사용할 수 있습니다.
- 기존의 고차함수에 인라인 함수로 변경하였습니다. 1. 성능 최적화: 인라인 함수는 컴파일 시 함수 호출 부분에 함수의 본문 코드가 직접 삽입됩니다. 이로 인해 함수 호출에 따른 오버헤드가 줄어들어 성능이 향상될 수 있습니다. 2. 람다 표현식 오버헤드 감소: 특히 고차 함수에서 람다 표현식을 사용할 때 발생할 수 있는 오버헤드를 줄이는 데 효과적입니다. 3. 코드 최적화: 작은 크기의 유틸리티 함수나 반복적으로 호출되는 간단한 연산에 적합합니다.
@ComponentclassFunctionComponent {
/**
* 인라인 고차함수 구조
*/privateinlinefuninlineHigherOrderFun(x: Int, y: Int, op: (Int, Int) -> Int): Int = op(x, y)
/**
* 인라인 고차함수 호출 : 함수 파라미터
*/funinlineHigherOrderFunCall() {
// 덧셈 연산val sum = higherOrderFun(5, 3) { a, b -> a + b }
println(sum) // 출력: 8// 곱셈 연산val product = higherOrderFun(5, 3) { a, b -> a * b }
println(product) // 출력: 15
}
}
💡 연산자 함수(operator fun) - Kotlin에서 연산자 오버로딩을 구현하기 위해 사용되는 특별한 함수입니다. - fun 키워드 앞에 operator 키워드를 사용하여 선언하며 기존 연산자(+, -, *, / 등)의 동작을 사용자 정의 클래스에 맞게 재정의할 수 있습니다.
💡 operator fun 연산자의 오버로딩 함수
주요 연산자 함수
설명
plus()
+ 연산자
minus()
- 연산자
times()
* 연산자
div()
/ 연산자
get()
[] 인덱스 연산자
inc()
++ 증가 연산자
dec()
-- 감소 연산자
classPoint(var x: Int, var y: Int) {
// + 연산자 오버로딩operatorfunplus(other: Point): Point {
return Point(x + other.x, y + other.y)
}
// - 연산자 오버로딩operatorfunminus(other: Point): Point {
return Point(x - other.x, y - other.y)
}
}
funmain() {
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)
}
[ 더 알아보기 ] 💡 중위 표기법(Infix Notation) - 일반적인 함수 호출 방식인 obj.function(param) 대신 obj function param 형태로 함수를 호출할 수 있게 해주는 표기법입니다. - 더 자연스럽고 읽기 쉬운 코드를 작성할 수 있게 해 줍니다. - 주로 두 객체 간의 연산이나 관계를 표현할 때 사용됩니다.
- 함수 호출 시 생략할 수 있는 매개변수를 의미합니다. - Kotlin에서는 매개변수에 물음표(?)를 붙여 nullable 타입으로 선언하여 구현합니다.
/**
* optional 파라미터를 사용한 함수
*/privatefunoptionalParam(name: String, age: Int?) {
if (age != null) {
println("$name is $age years old")
} else {
println("$name's age is unknown")
}
}
funoptionalParamCall() {
optionalParam("John", 25) // age 값 제공
optionalParam("Jane", null) // age 값을 null로 제공
}
💡 가변 인자 (Vararg Parameter) - 함수 내에 파라미터에 vararg 키워드를 사용하여 가변 개수의 인자를 받는 함수를 정의합니다.
1. 타입: vararg 키워드 뒤에 지정된 타입으로 제한됩니다. 예를 들어, 'vararg numbers: Int'는 정수형 인자만 받을 수 있습니다. 2. 개수: 가변 인자 함수는 0개 이상의 인자를 받을 수 있습니다. 즉, 인자를 전혀 전달하지 않거나, 하나 또는 여러 개의 인자를 전달할 수 있습니다.
/**
* vararg 파라미터를 이용하여 가변 인자 함수
*/privatefunvarargSumParam(vararg numbers: Int): Int = numbers.sum()
/**
* vararg 파라미터를 이용하여 가변 인자 함수 호출
*/funvarargParamCall() {
varargSumParam() // 인자 없이 호출 가능
varargSumParam(1) // 단일 인자
varargSumParam(1, 2, 3) // 여러 인자
varargSumParam(*intArrayOf(1, 2, 3)) // 배열을 전개 연산자(*)와 함께 사용
}
- 객체를 이용해 ‘작업을 수행하고 결과를 반환’합니다. 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 함수를 이용한 예시
*/funletScopeFun() {
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? = nullval 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]가 출력
}
💡 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
dataclassPerson(var name: String, var age: Int)
/**
* 범위 함수의 사용예시를 관리하는 컴포넌트
*/@ComponentclassScopeFunctionComponent {
/**
* run 함수를 이용한 예시
*/funrunScopeFun() {
// 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
}
}
💡 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
dataclassPerson(var name: String, var age: Int)
/**
* 범위 함수의 사용예시를 관리하는 컴포넌트
*/@ComponentclassScopeFunctionComponent {
funwithScopeFun() {
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
}
}
- 객체에 대한 추가 작업을 수행하며, 객체 자체를 반환합니다. - 객체의 속성을 변경하지 않고 부가적인 작업을 수행할 때 유용합니다. - 특히나 디버깅이나 로깅 목적으로 자주 사용됩니다.
💡 also 함수 기본 구조
- 'also' 함수는 수신 객체 지정 람다의 한 형태입니다. 객체를 받아 부가적인 작업을 수행하고, 그 객체를 그대로 반환합니다. - 람다 내에서 수신 객체는 'it'으로 참조됩니다. - 주로 객체의 상태를 변경하지 않는 부가적인 작업(로깅, 유효성 검사 등)에 사용됩니다.
valobject = SomeClass().also {
// 객체에 대한 부가적인 작업 수행// 'it'을 통해 객체 참조// 객체 자체가 반환됨
}
💡 also 함수를 이용한 예시
1. 객체 초기화 후 로깅 예시 - Person 객체를 생성하고 초기화한 후, 객체의 상태를 로그로 출력합니다. 2. 컬렉션 처리 예시 리스트를 생성하고 정렬한 후, 정렬된 상태를 출력합니다.
package com.blog.kotlinspringbootform.component
import org.springframework.stereotype.Component
dataclassPerson(var name: String, var age: Int)
/**
* 범위 함수의 사용예시를 관리하는 컴포넌트
*/@ComponentclassScopeFunctionComponent {
/**
* also 함수를 이용한 예시
*/funalsoScopeFun() {
// 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")
}
}