초보 개발자의 성장기

[부스트코스] [부스트코스][코틀린 프로그래밍 기본 1/2] 한아아지와 함께하는 kotlin - 함수형 프로그래밍 (3) 익명 함수와 인라인 함수 본문

IT 강의 리뷰/코틀린

[부스트코스] [부스트코스][코틀린 프로그래밍 기본 1/2] 한아아지와 함께하는 kotlin - 함수형 프로그래밍 (3) 익명 함수와 인라인 함수

개발자 김케빈 2021. 1. 22. 15:37

 

이번 시간에는 익명 함수와 인라인 함수에 대해 공부해보겠습니다.


www.boostcourse.org/mo132/lecture/59987

 

코틀린 프로그래밍 기본 1

부스트코스 무료 강의

www.boostcourse.org

www.boostcourse.org/mo132/lecture/64782

 

코틀린 프로그래밍 기본 1

부스트코스 무료 강의

www.boostcourse.org


1. 익명 함수 (anonymous functions)

1.1. 익명함수란?

익명함수는 함수가 이름이 없는 것을 의미합니다.

 

1.2. 익명 함수 예시 코드


fun(x: Int, y: Int): Int = x + y // 함수 이름이 생략된 익명 함수

val add: (Int, Int) -> Int = fun(x, y) = x + y // 익명 함수를 사용한 add 선언
val result = add(10, 2) // add의 사용

val add = fun(x: Int, y: Int) = x + y // 람다식과 매우 흡사

val add = {x: Int, y: Int -> x + y } // 람다식

※ 일반 익명 함수에서는 return, break, continue가 사용 가능하지만 람다식에서는 사용하기 어려움!

 

2. 인라인 함수 (inline funtions)

2.1. 인라인 함수란?

  • 인라인(inline) 함수는 이 함수가 호출되는 곳에 내용을 모두 복사해 넣어 함수의 분기 없이 처리되기 때문에 코드의 성능을 높일 수 있습니다.
  • 인라인 함수는 코드가 복사되어 들어가기 때문에 대개 내용은 짧게 작성합니다.
  • 인라인 함수는 람다식 매개변수를 가지고 있는 함수 형태를 권장합니다. 

2.2. 인라인 함수 코드 예시


inline fun shortFunc(a: Int, out: (Int) -> Unit) {
    println("Before calling out()")
    out(a)
    println("After calling out()")
}

fun main() {
    // 인라인 함수 shortFunc의 내용이 복사되어 들어감
    shortFunc(3) { println("First call: $it") }
    shortFunc(5) { println("Second call: $it") }
}

/*
결과
First call: Before calling 3
Second call: After calling 5
*/

2.3. 인라인 함수 장단점

인라인 함수를 쓰면 메모리 할당과 오버헤드에 효과적이고 메서드를 호출하는게 아니라 복사하지만 성능의 향상으로
이어지지만 컴파일시 변환되는 Byte Code의 길이는 더 길어진다.
코드가 복사되므로 내용이 많은 함수에 사용하면 코드가 늘어나서 가독성이 떨어집니다.

2.4. noinline 키워드

단지 몇개의 람다만 인라인 함수로 전달하고자 할때는 해당하는 함수 파라미터에만 noinline modifier 를 쓸 수 있다.
일부 람다식 함수를 inline 되지 않게 도와줍니다.

 

※ 코드 기본 사용

inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) { ... }


inline fun shortFunc(a: Int, noinline out: (Int) -> Uint) {
	println("Hello")
    out(a)
    println("Goodbye")
}

fun main() {
	shortFunc(3) {
    	println("a: $it")
        return
    }
}

2.5.  Non-local returns

람다를 종료하기 위해서는 라벨 을 반드시 써야 하며 람다는 둘러싼 함수(enclosing function) 에서 리턴을 할 수 없기 때문에 return 만 쓰는것은 금지된다.

 

※ 코드 기본 예시


fun ordinaryFunction(block: () -> Unit) {
	println("hi!")
}

fun foo() {
	ordinaryFunction {
    	return // ERROR: cannot make `foo` return here 
    }
}

2.6. crossinline 키워드

비지역 반환을 금지하려면 crossinline이라는 키워드를 람다식 함수 앞에 사용해

함수의 본문 블록에서 return이 사용되는 것을 금지할 수 있습니다.


inline fun shortFunc(a: Int, crossinline out: (Int) -> Uint) {
	println("Hello")
    out(a)
    println("Goodbye")
}

fun main() {
	shortFunc(3) {
    	println("a: $it")
        // return
    }
}

2.7. Inline properties

inline modifier 는 backing field 를 가지지 않은 프로퍼티의 접근자에 쓰일 수 없다.

각각의 프로퍼티 접근자에는 쓸 수 있다.


val foo: Foo
	inline get() = Foo()
    
var bar: Bar
	get() = ...
    inline set(v) { ... }


// 접근자 두개를 다 가진 프로퍼티 전체를 위해서는 쓸 수 있다.
inline var bar: Bar
	get() = ...
    set(v) { ... }

3. 확장 함수

3.1. 확장 함수란?

클래스의 멤버 함수를 외부에서 더 추가할 수 있게 도와주는 함수입니다.
기존 표준 라이브러리를 수정하지 않고도 확장이 가능합니다.

※ 코드 기본 사용

fun 확장대상.함수명(매개변수, ...): 반환값 {

    ...

    return 값

}

 

3.2. 확장 함수 코드 예시


fun main() {
    val source = "Hello World!"
    val target = "Kotlin"
    println(source.getLongString(target))
}

// String을 확장해 getLongString 추가
fun String.getLongString(target: String): String =
        if (this.length > target.length) this  else target
        // this는 확장 대상에 있던 자리의 문자열 source 객체를 나타냄
        

확장 함수를 만들 때 만일 확장하려는 대상에 동일한 이름의 멤버 함수 혹은 메서드가 존재한다면

    항상 확장 함수보다 멤버 메서드가 우선으로 호출됩니다.

Comments