함수의 이름이 없는 것!
// 함수의 이름이 생략된 익명 함수
fun (x: Int, y: Int): Int = x + y
package chap04.section3
fun main() {
// 익명 함수를 사용한 add 선언
val add: (Int, Int) -> Int = fun(x, y) = x + y
val result = add(10, 2) // add의 사용
println(result) // 12
}
package chap04.section3
fun main() {
val add = fun(x: Int, y: Int) = x + y // 일반 익명 함수
val addLambda = { x: Int, y: Int -> x + y } // 람다식
println(add(5, 3))
println(addLambda(5, 3))
}
cf) 일반 익명 함수에서는 return, break, continue 사용이 가능하지만, 람다식에서는 사용하기 어렵다. (라벨 표기법과 같이 사용해야 함)
package chap04.section3
fun shortFunc(){
println("Hello")
}
fun main() {
shortFunc()
}
Tools > Kotlin > Show Kotlin Bytecode > Decompile 해서 자바 클래스를 확인해보면, main 함수에서 shortFunc 함수를 호출하고 있음을 확인할 수 있다.
package chap04.section3
inline fun shortFunc(){
println("Hello")
}
fun main() {
shortFunc()
}
반면에, 인라인 함수를 디컴파일 해보면, shortFunc 함수를 호출하는 것이 아니라 그 내용을 모두 복사한다는 걸 확인할 수 있다!!
Inlining works best for functions with parameters of functional types
👉 이 경고 문구를 보면, 인라인 함수는 아래 예시처럼 람다식을 매개변수로 사용할 때 성능이 가장 좋다는 걸 알 수 있다.
package chap04.section3
inline fun shortFunc(a: Int, out: (Int) -> Unit){
println("Hello")
out(a)
}
fun main() {
//shortFunc(3, { a -> println("a: $a") })
//shortFunc(3) { a -> println("a: $a") }
shortFunc(3) { println("a: $it") }
}
Hello
a: 3
inline fun sub(out1: () -> Unit, noinline out2: () -> Unit) { ... }
inline fun shortFunc(a: Int, noinline out: (Int) -> Unit){
println("Hello")
out(a) // noinline
}
fun main() {
shortFunc(3) { println("a: $it") }
}
package chap04.section3
inline fun shortFunc(a: Int, out: (Int) -> Unit){
println("Hello")
out(a)
println("Goodbye")
}
fun main() {
shortFunc(3) {
println("a: $it")
}
}
Hello
a: 3
Goodbye
여기서 실수로 람다식에 return문을 작성하면 어떻게 될까?
package chap04.section3
inline fun shortFunc(a: Int, out: (Int) -> Unit){
println("Hello")
out(a)
println("Goodbye") // 실행되지 않음.
}
fun main() {
shortFunc(3) {
println("a: $it")
return // 람다 함수의 종료 (비지역 반환)
}
}
Hello
a: 3
람다 함수 out이 종료되어 그 이후의 내용이 실행되지 않는다! 이를 비지역 반환이라 부른다.
람다식 매개변수 앞에 crossinline 키워드를 붙여주면, 해당 람다식 함수를 호출할 때 return문을 실행할 수 없게 된다. 즉, 비지역 반환을 금지하는 것이다!
📌 부연 설명
코틀린에서는 익명 함수를 종료시키기 위해 return을 사용할 수 있다. 이때 특정 반환값 없이 return만 사용해야 한다. 그렇다면, 람다식 함수를 빠져나오려면 어떻게 해야 할까? 람다식 함수를 인자로 사용하는 함수는 의도치 않게 람다식 함수 바깥에 있는 함수도 같이 반환이 되어버릴 수 있는데, 이를 비지역 반환이라고 한다. 이를 금지하려면, 람다식 함수 앞에 crossline 키워드를 사용해, 함수의 본문 블록에서 return이 사용되는 것을 금지할 수 있다.
클래스에는 다양한 함수가 정의되어 있다. 이것은 클래스의 멤버 메서드라고도 불린다. 그런데 기존 멤버 메서드는 아니지만 내가 원하는 함수를 하나 더 포함시켜 확장하고 싶을 때가 있을 것이다. 코틀린에서는 이처럼 클래스처럼 필요로 하는 대상에 함수를 더 추가할 수 있는 확장 함수(extension function)라는 개념을 제공하고 있다. 요약하자면, 확장함수는 클래스의 멤버 함수를 외부에서 더 추가할 수 있는 개념이다!
fun 확장대상.함수명(매개변수, ...): 반환값 {
...
return 값
}
package chap04.section3
// String을 확장하여 getLongString이라는 멤버 함수 추가
fun String.getLongString(target: String): String =
if(this.length > target.length) this else target
fun main() {
val source = "Hello World!"
val target = "Kotlin"
println(source.getLongString(target)) // 더 긴 문자열을 출력함.
}
this는 확장 대상에 있던 자리의 문자열인 source 객체를 나타낸다. 기존의 표준 라이브러리를 수정하지 않고도 클래스의 멤버 함수를 확장할 수 있어서 매우 유용한 개념이다!
확장 함수를 만들 때 만일 확장하려는 대상에 동일한 이름의 멤버 함수 혹은 메서드가 존재한다면, 항상 확장 함수보다 멤버 메서드가 우선으로 호출된다는 것도 추가적으로 알아두자.
클래스의 멤버 호출 시 사용하는 점(.)을 생략하고, 함수 이름 뒤에 소괄호를 생략해, 직관적인 이름을 사용할 수 있는 표현법
📌 중위 함수의 조건
package chap04.section3
fun main() {
// 일반 표현법
//val multi = 3.multiply(10)
// 중위 표현법
val multi = 3 multiply 10
println("multi: $multi")
}
// Int를 확장해서 multiply() 함수 추가
infix fun Int.multiply(x: Int): Int { // 중위 함수
return this * x
}
이처럼 중위 함수는 일종의 연산자를 구현할 수 있는 함수인데, 특히 비트 연산자에서 자주 사용한다고 한다.
📌 재귀 함수의 필수 조건
package chap04.section3
fun factorial(n: Int): Long {
return if(n == 1) n.toLong() else n * factorial(n - 1)
}
fun main() {
val number = 4
val result: Long = factorial(number)
println("Factorial: $number! => $result")
}
Factorial: 4! => 24
여기서 n이 너어어어어무 커지면, 계속해서 스택 프레임에 그 내용이 쌓이면서 스택 오버플로우가 발생할 수 있다!
꼬리 재귀를 사용하면 팩토리얼의 값을 그때 그때 계산하므로 스택 공간을 낭비하지 않아도 되므로, 일반 재귀함수보다 훨씬 더 안전한 코드가 된다.
package chap04.section3
tailrec fun factorial(n: Int, run: Int = 1): Long {
return if(n == 1) run.toLong() else factorial(n - 1, run * n)
}
fun main() {
val number = 5
println("Factorial: $number! => ${factorial(number)}")
}
📌 꼬리 재귀에 대한 영상을 봤는데,, 아직 완전히 이해 못 했다,, 다음에 꼭 다시 보자!!!