함수 몸체부 내에 정의할 수 있는 대상
- 지역 함수
- 지역 클래스: 보통 함수 기능이 커지면 클래스로 변환하여 정의해서 사용하므로 실제 사용할 일은 별로 없다.
- object 표현식으로 정의해서 객체를 사용할 수 있다.
단일 표현식
함수 코드 블록의 하나의 라인으로 처리되는 경우를 단일 표현식이라고 한다.
- 간단한 표현식만 있는 경우는 블록을 생략하고 한 줄로 작성하는 단일표현식으로 작성할 수도 있다.
- 보통 = 연산자 다음에 표현식을 사용해서 처리한다.
- 단일 표현식으로 함수 코드 블록을 구성하면 단일 표현식을 추론해 반환 자료형을 추론할 수 있으므로 반환 자료형을 생략한다.
함수 오버로딩
함수 식별자는 함수 이름과 시그니처인 함수 매개변수의 개수와 자료형으로 구성한다. 그래서 같은 이름의 함수를 여러 개 정의할 수 있다. 이것을 함수 오버로딩(function overloading)이라고 한다. 즉, 함수 식별자가 다른 것을 여러 개 정의한다는 뜻이다. 함수를 호출할 때는 실제 인자의 개수를 맞추면 내부적으로 해당하는 함수를 선택해서 실행해준다.
여러 개의 함수를 정의하는 방식이 다 좋은 것은 아니다. 이런 함수 오버로딩을 줄이려면 함수를 정의할 때 초깃값을 사용하거나 매개변수를 가변인자로 변경해서 작성하면 된다. 여러 개의 매개변수가 같은 자료형이면 가변인자를 정의해서 하나의 함수로 처리하는 것이 좋다.
가변인자(variable argument)
인자의 개수가 미정일 때는 가변인자를 사용한다. 가변인자를 작성할 때는 매개변수 이름 앞에 varag 예약어를 사용한다. 가변인자는 이 매개변수 이름으로 여러 개의 원소를 가지는 배열로 처리한다.
가변인자에 대한 변수의 자료형을 코드 블록에서 확인하면 배열이고 실제 합산을 하려면 이 배열을 순환해서 모든 원소를 더해야 한다.
배열로 정의된 것을 가변인자로 처리하려면 별표(*)를 붙여 모든 원소가 전달되어야 가변인자가 처리된다. 이 연산자를 스프레드 연산자(spread operator)라고 한다. 주의할 점은 배열일 경우에만 이 연산자가 작동한다는 것이다.
매개변수에 초깃값 지정
fun defaultrg(x:Int = 100, y:Int = 200) = x+y
println("인자 전달없음=" + defaultArg())
// 인자 전달없음=300
렉시컬 스코프(lexical scope)
외부함수와 내부함수는 각각 스코프를 구성한다. 내부함수는 외부함수의 스코프를 참조할 수 있으므로 스코프 계층이 생긴다. 이런 계층에서 변수를 검색하는 방법을 렉시컬 스코핑이라고 한다.
렉시컬 스코프의 처리기준
항상 자기 함수 내부(local)를 조회하고 없으면 외부함수(non-local)를 조회하고 없으면 전역인 패키지의 변수(global)를 찾는다.
지역변수는 함수가 실행될 때 생겼다가 함수가 종료되면 사라진다.
코틀린에서 함수 정의 없이 바로 실행하는 함수를 만드는 두 가지 방법
1) 익명함수(anonymous function)
2) 람다표현식(Lambda expression)
익명함수는 함수 정의와 동일하지만 함수의 이름을 가지지 않는다. 대신 일회성으로 처리하는 용도로 사용한다.
변수에 할당하고 변수 이름으로 조회해서 실행하는 것이 가능하다.
익명함수 내부에 지역 익명함수 정의
- 익명함수 내부에 익명함수 지역함수로 정의할 수 있다.
- 함수의 반환자료형을 처리할 때 함수를 반환할 수 있다.
- 함수의 매개변수로 함수자료형을 정의하면 함수를 호출할 때 익명함수를 인자로 전달할 수 있다.
람다표현식
코틀린에서 람다표현식은 상수처럼 사용하는 함수를 의미한다.
익명함수도 있지만 람다표현식을 사용하는 방식이 더 간편하게 함수를 정의하고 상수처럼 인자나 반환값 등으로 전달하기 편리하다.
- 예약어가 없고 함수 이름도 없는 게 특징이다.
- 코드 블록인 중괄호 안에 직접 매개변수와 표현식을 작성한다.
- 람다표현식에서 매개변수는 괄호처리를 하지 않는다.
- 매개변수가 없을 때는 매개변수를 생략하고 표현식만 작성한다.
- 람다표현식을 작성하고 바로 실행하려면 람다표현식 다음에 호출연산자인 괄호를 사용해서 매개변수에 해당 인자를 전달한다.
- 람다표현식에는 기본적으로 return문을 사용할 수 없지만, inline 함수로 사용할 때는 return 문을 사용할 수 있다. 그 이유는 실제 람다표현식 내부 로직이 호출된 곳에 코드로 삽입되기 때문이다.
- 람다표현식에서 중간에 함수를 빠져나갈 경우에는 레이블 된 return 문을 사용할 수 있다. 이때는 반환값이 아니라 현재 처리하는 람다표현식을 종료하는 조건으로만 사용한다.
- 람다표현식에서 매개변수가 하나만 있을 경우 별도의 매개변수를 지정하지 않고 하나의 매개변수라는 의미로 it을 제공한다.
- 람다표현식을 작성하고 바로 실행하면 람다표현식이 전달되는 것이 아니라 람다표현식이 실행된 결괏값이 전달되어 이를 출력한다.
- 람다표현식을 작성해서 다시 사용하려면 익명함수를 작성해서 변수에 저장하는 방식을 그대로 활용한다.
- 함수를 정의할 때 매개변수에 함수 자료형을 지정하면 이 함수를 호출할 때 람다표현식을 전달해서 처리할 수 있다.
- 람다표현식은 익명함수보다 더 축약된 표현이고 return 문을 사용하지 않는 것을 빼면 동일하다.
- 보통 재사용할 때는 함수를 정의해서 사용하지만, 함수를 인자로 전달하고 반환값을 처리할 때는 람다표현식으로 처리한다. 익명함수는 람다표현식보다 내부 코드가 많아질 때 사용하는 것이 좋다.
클로저(Closure)
외부함수 내에 내부함수를 정의하고 단순히 내부함수를 실행하지 않고 이 내부함수를 반환한다. 이때 내부함수는 외부함수의 지역변수를 사용할 수 있다. 반환된 내부함수가 실행될 동안 외부함수의 지역변수를 계속 사용한다. 이때 외부함수의 지역변수를 자유변수라고 하고 이런 환경을 클로저(closure)라고 한다.
함수 참조
지역함수가 일반 함수일 때는 바로 반환할 수 없다. 대신 리플렉션의 함수 참조를 사용해 함수 레퍼런스(함수 객체)를 조회해서 반환할 수 있다. 함수를 반환하는 것은 아직 함수를 실행한 결과가 아닌 함수 자체를 반환하는 것이다.
함수 자료형
함수도 자료형을 사용해서 변수나 반환값 등으로 사용할 수 있는 것은 함수도 1급 객체이기 때문이다. 그래서 함수를 변수에 할당하거나 반환값을 사용할 때 함수 자료형을 명확히 지정해서 사용해야 한다.
매개변수 자료형에 함수가 전달될 때는 함수 자료형을 지정한다. 함수자료형 지정은 매개변수는 괄호 안에 표시하고 -> 다음에 반환값을 지정한다.
변수에 함수가 할당될 때는 익명함수, 람다표현식, 함수 참조에서 정의된 것을 확인하고 타입추론이 가능하다.
코틀린에서는 함수자료형을 인터페이스로 제공한다.
함수 정의도 함수 참조를 사용해서 변수에 할당할 수 있다. 이때도 동일한 함수 자료형을 지정해서 처리해야 한다.
invoke 메서드
코틀린은 널이 불가능한 자료형을 확장해서 널이 가능한 자료형으로 만들 수 있다.
널이 들어온 경우 안전호출을 처리하기 위해 invoke 메서드로 처리한다.
함수 자료형도 널 자료형이 가능하다.
invoke 메서드가 실행되면 함수의 반환 자료형에 속하는 객체를 반환한다.
val innerFunc : (Int) -> ((Int) -> Int)? = {n -> null}
println(innerFunc(10)?.invoke(20))
// null
val innerFunc : (Int) -> ((Int) -> Int)? = {n -> {m -> n}}
println(innerFunc(10)?.invoke(20))
// 10
'Kotlin > 스터디' 카테고리의 다른 글
코틀린 스터티 - 8장 (0) | 2023.09.03 |
---|---|
코틀린 스터디 - 6장 (0) | 2023.08.20 |
코틀린 스터디 - 5장 (0) | 2023.08.13 |
코틀린 스터디 3장 (0) | 2023.07.30 |
코틀린 스터디 - 2장 (0) | 2023.07.23 |