오버로딩과 고차 함수
코틀린 Step02 - 오버로딩과 고차 함수
산술 연산자 오버로딩
코틀린에서 관례를 사용하는 가장 단순한 예는
산술 연산자자바에서는 원시 타입에 대해서만 산술 연산자 사용 가능하고, String에 대해 + 연산자를 사용 가능
이항 산술 연산 오버로딩
operator변경자를 추가해 plus 함수를 선언하고 나면 + 기호로 두 Point 객체를 더할 수 있음
// case 1. 연산자를 자체 함수로 정의하기
data class Point(val x: Int, val y: Int) {
operator fun plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}
}
// case 2. 연산자를 확장 함수로 정의하기
data class Point2(val x: Int, val y: Int)
operator fun Point2.plus(other: Point2): Point2 {
return Point2(x + other.x, y + other.y)
}
@Test
fun `이항 산술 연산 오버로딩`() {
val p1 = Point(10, 20)
val p2 = Point(30, 40)
assertEquals(Point(40, 60), p1 + p2)
val p3 = Point2(10, 20)
val p4 = Point2(30, 40)
assertEquals(Point2(40, 60), p3 + p4)
}ℹ️ 오버로딩 가능한 이항 산술 연산자
a * b
times
a / b
div
a % b
mod
a + b
plus
a - b
minus
연산자를 정의할 때 두 피연산자가(연산자 함수의 두 파라미터)가 같은 타입일 필요는 없다.
또는 연산자 함수의 반환 타입이 꼭 두 피연산자 중 하나와 일치해야만 하는 것도 아니다.
복합 대입 연산자 오버로딩
+=,-=등의 연산자는 복합 대입(compound assignment) 연산자라 불림
코틀린 표준 라이브러리는 변경 가능한 컬렉션에 대해 plusAssign을 정의
단항 연산자 오버로딩
단항 연산자 오버로딩하는 절차도 이항 연산자와 동일
ℹ️ 오버로딩할 수 있는 단항 산술 연산자
+a
unaryPlus
-a
unaryMinus
!a
not
++a, a++
inc
--a, a--
dec
비교 연산자 오버로딩
동등성 연산자: equals
코틀린이
==연산자 호출을equals메소드 호출로 컴파일
≠연산자를 사용하는 식도equals호출로 컴파일
순서 연산자: compareTo
자바에서 정렬, 최댓값, 최솟값 등 값을 비교해야 하는 알고리즘에 사용할 클래스는
Comparable인터페이스 구현이 필요하듯, 코틀린도 같은 Comparable 인터페이스를 지원
코틀린 표준 라이브러리의 compareValuesBy 함수를 사용해 compareTo를 쉽고 간결하게 정의할 수 있다.
컬렉션과 범위에 대해 쓸 수 있는 관례
in 관례
In은 객체가 컬렉션에 들어있는지 검사.
in연산자와 대응하는 함수는contains
rangeTo 관례
범위를 만들려면
..구문을 사용
예를 들어 1..10은 1부터 10까지 모든 수가 들어있는 범위를 가리킨다.
![]()
https://livebook.manning.com/book/kotlin-in-action/chapter-7/65
for 루프를 위한 iterator 관례
코틀린에서는
iterator메소드를 확장 함수로 정의 가능이런 성질로 인해 일반 자바 문자열에 대한 for 루프가 가능
구조 분해 선언과 component 함수
구조 분해를 사용하면 복합적인 값을 분해해서 여러 다른 변수를 한꺼번에 초기화 가능
구조 분해 선언은 함수에서 여러 값을 반환할 때 유용
👎🏻 여러 값을 한꺼번에 반환해야 하는 함수가 있다면 반환해야 하는 모든 값이 들어갈 데이터 클래스를 정의하고 함수의 반환 타입을 그 데이터 클래스로 바꿔주어야 한다.
👍🏼 구조 분해 선언 구문을 사용하면 이런 함수가 반환하는 값을 쉽게 풀어서 여러 변수에 넣을 수 있다.
구조 분해 선언과 루프
함수 본문 내의 선언문 뿐 아니라,
변수 선언이 들어갈 수 있는 장소라면 어디든 구조 분해 선언을 사용 가능
두 가지 코틀린 관례를 활용한 예시
객체를 이터페이션하는 관례
구조 분해 선언
프로퍼티 접근자 로직 재활용: 위임 프로퍼티
위임 프로퍼티(
delegated property)를 사용하면 값을 뒷받침하는 필드에 단순히 저장하는 것보다더 복잡한 방식으로 작동하는 프로퍼티를 쉽게 구현 가능하다.
또한, 그 과정에서 접근자 로직을 매번 재구현할 필요도 없다.
위임은 객체가 직접 작업을 수행하지 않고, 다른 도우미 객체가 그 작업을 처리하게 맡기는 디자인 패턴을 말한다.
이때 작업을 처리하는 도우미 객체를
위임 객체라고 부른다.
위임 프로퍼티 사용: by lazy()를 사용한 프로퍼티 초기화 지연
고차 함수 정의
고차 함수는 다른 함수를 인자로 받거나 함수를 반환하는 함수코틀린에서는 람다나 함수 참조를 사용해 함수를 값으로 표현 가능
따라서 고차 함수는 람다나 함수 참조를 인자로 넘길 수 있거나, 람다나 함수 참조를 반환하는 함수
함수 타입
함수 타입을 정의하려면 함수 파라미터의 타입을 괄호 안에 넣고,
그 뒤에 화살표(→)를 추가한 다음, 함수의 반환 타입을 지정

https://livebook.manning.com/book/kotlin-in-action/chapter-8
Unit 타입은 의미 있는 값을 반환하지 않는 함수 반환 타입에 쓰는 특별한 타입
그냥 함수를 정의한다면 함수의 파라미터 목록 뒤에 오는 Unit 반환 타입 지정을 생략해도 되지만,
⚠️ 함수 타입을 선언할 때는 반환 타입을 반드시 명시해야 하므로 Unit을 필수로 명시
인자로 받은 함수 호출
인자로 받은 함수를 호출하는 구문은 일반 함수를 호출하는 구문과 동일
디폴트 값을 지정한 함수 타입 파라미터나 널이 될 수 있는 함수 타입 파라미터
파라미터를 함수 타입으로 선언할 때도 디폴트 값을 정할 수 있다.
함수를 함수에서 반환
다른 함수를 반환하는 함수를 정의하려면 함수의 반환 타입으로 함수 타입을 지정
함수를 반환하려면 return 식에 람다나 멤버 참조나 함수 타입의 값을 계산하는 식 등을 넣으면 된다.
람다를 활용한 중복 제거
함수 타입과 람다 식은 재활용하기 좋은 코드를 만들 때 쓸 수 있는 훌륭한 도구
웹 사이트 방문 기록을 분석하는 예시
인라인 함수: 람다의 부가 비용 없애기
inline변경자를 함수에 붙이면 컴파일러는 그 함수를 호출하는 모든 문장을함수 본문에 해당하는 바이트 코드로 바꿔준다.
인라이닝이 작동하는 방식
특정 함수를
inline선언하면 그 함수의 본문이 인라인
함수를 호출하는 코드를 함수를 호출하는 바이트코드 대신에 함수 본문을 번역한 바이트 코드로 컴파일
컬렉션 연산 인라이닝
코틀린 표준 라이브러리의 컬렉션 함수는 대부분 람다를 인자로 받는다.
filter, map은 인라인 함수
두 함수의 본문은 인라이닝되며, 추가 객체나 클래스 생성은 없다.
하지만 이 코드는 리스트를 걸러낸 결과를 저장하는 중간 리스트를 만든다.
처리할 원소가 많아지면 중간 리스트를 사용하는 부가 비용도 걱정할 만큼 커진다.
asSequence를 통해 리스트 대신 시퀀스를 사용하면 중간 리스트로 인한 부가 비용은 줄어든다.각 중간 시퀀스는 람다를 필드에 저장하는 객체로 표현되며,
최종 연산은 중간 시퀀스에 있는 여러 람다를 연쇄 호출
함수를 인라인으로 선언해야 하는 경우
⚠️ inline 키워드의 이점을 배우고 나면 코드를 더 빠르게 만들기 위해 여기저기에서 inline을 사용하고 싶어질 것이다. 하지만❗️ 이는 좋은 생각이 아니다.
inline키워드를 사용해도 람다를 인자로 받는 함수만 성능이 좋아질 가능성이 높다.
1️⃣ 일반 함수 호출의 경우 JVM은 이미 강력하게 인라이닝을 지원한다.
JVM은 코드 실행을 분석해서 가장 이익이 되는 방향으로 호출을 인라이닝한다.
이런 과정은 바이트 코드를 실제 기계어 코드로 번역하는 과정(JIT)에서 일어난다.
이런 JVM의 최적화를 활용한다면 바이트 코드에서는 각 함수 구현이 정확히 한 번만 있으면 되고, 그 함수를 호출하는 부분에서 따로 함수 코드를 중복할 필요가 없다.
2️⃣ 인라인 함수는 바이트 코드에서 각 함수 호출 지점을 함수 본문으로 대치하기 때문에 코드 중복이 생긴다.
게다가 함수를 직접 호출하면 스택 트레이스가 더 깔끔해진다.
3️⃣ inline 변경자를 함수에 붙일 때는 코드 크기에 주의를 기울여야 한다.
인라이닝하는 함수가 큰 경우 함수의 본문에 해당하는 바이트 코드를 모든 호출 지점에 복사해 넣고 나면 바이트코드가 전체적으로 아주 커질 수 있다.
🎬 그러면 함수를 인라인으로 언제 선언할까 ❓
람다를 인자로 받는 함수
람다를 인자로 받는 함수는 인라인으로 선언하면 성능이 향상될 수 있다.
함수 호출의 오버헤드를 줄이고, 람다를 인라인으로 대체하여 추가 객체 생성을 피할 수 있다.
고차 함수
고차 함수(다른 함수를 인자로 받거나 반환하는 함수)는 인라인으로 선언하면 호출 비용을 줄일 수 있다.
성능이 중요한 경우
함수 호출이 빈번하고, 성능이 중요한 경우 호출 오버헤드를 줄일 수 있다.
작은 함수
함수 본문이 작고, 여러 번 호출되는 경우 코드 중복이 크게 증가하지 않으면서 성능을 향상
자원 관리를 위해 인라인된 람다 사용
자바의
try-with-resource문은 코틀린에서 제공하지 않는다.대신 같은 기능을 제공하는
use라는 함수가 코틀린 표준 라이브러리 안에 있다.
use 함수는 닫을 수 있는(closeable) 자원에 대한 확장 함수며, 람다를 인자로 받는다.
use는 람다를 호출한 다음에 자원을 닫아준다.
고차 함수 안에서 흐름 제어
람다 안의 return문: 람다를 둘러싼 함수로부터 반환
람다 안에서 return을 사용하면 람다로부터만 반환되는 게 아니라 그 람다를 호출하는 함수가 실행을 끝내고 반환된다.
자신을 둘러싸고 있는 블록보다 더 바깥에 있는 다른 블록을 반환하게 만드는 return 문을 non-local return이라 부른다.
이렇게 return이 바깥쪽 함수를 반환시킬 수 있는 때는 람다를 인자로 받는 함수가 인라인 함수인 경우뿐
forEach는 인라인 함수이므로 람다 본문과 함께 인라이닝
따라서 return 식이 바깥쪽 함수(lookForAlice)를 반환시키도록 쉽게 컴파일
람다로부터 반환: 레이블을 사용한 return
람다 식에서도
local return을 사용할 수 있다.
람다 안에서 local return은 for루프의 break와 비슷한 역할을 한다.
단,
local return과non-local return을 구분하기 위해 레이블(label)을 사용해야 한다.
✅ 람다에 레이블을 붙여서 사용하는 대신 람다를 인자로 받는 인라인 함수의 이름을 return 뒤에 레이블로 사용해도 된다.
무명 함수: 기본적으로 로컬 return
무명 함수는 코드 블록을 함수에 넘길 때 사용할 수 있는 다른 방법
무명 함수는 일반 함수와 비슷해 보이지만, 차이는 함수 이름이나 파라미터 타입을 생략할 수 있다는 점 뿐
무명 함수 안에서 레이블이 붙지 않은 return 식은 무명 함수 자체를 반환시킬 뿐 무명 함수를 둘러싼 다른 함수를 반환시키지 않는다.
return에 적용되는 규칙은 단순히 fun 키워드를 사용해 정의된 가장 안쪽 함수를 반환시킨다는 점
람다 식의 구현 방법이나 람다 식을 인라인 함수에 넘길 때 어떻게 본문이 인라이닝 되는지 등의 규칙을 무명 함수에도 모두 적용 가능
Last updated