코틀린 메서드
- 확장 함수
- infix 함수 (중위 함수)
- inline 함수
- 지역 함수
확장 함수
우선 확장 함수가 나타나게 된 배경부터 알아보자.
Kotlin은 Java와 100% 호환하는 것을 목표로 한다.
이런 목표로 인해 기존 Java 코드 위에 자연스럽게 Kotlin 코드를 추가할 수 있는 방안에 대해 고민하게 된다.
이 부분이 가능하다면 Java 코드에서 새로운 기능을 만들 때 val/var, null-safety 같은 코틀린의 특성을 활용할 수 있게 된다는 장점을 갖는다.
이러한 부분을 충족시키기 위해 고민하다가 나온 방안이 어떤 클래스 안에 있는 메서드처럼 호출할 수 있지만, 함수는 밖에 만들 수 있도록 한다. 이다.
즉 밖에 있는 메서드를 마치 클래스 안에 있는 멤버 함수처럼 쓸 수 있도록 하자는 것이다.
위 개념이 확장 함수이다.
1
2
3
4
5
6
7
8
fun main() {
val str = "ABC"
println(str.lastChar())
}
fun String.lastChar(): Char {
return this[this.length - 1]
}
- 위의 예시는 문자열의 마지막 글자를 가져오는 메서드이다.
- String.lastChar()
- String 클래스를 확장하는 것이다.
- this를 이용해 실제 클래스의 값에 접근할 수 있다. (수신 객체)
- str.lastChar()
- 마치 원래 String에 존재했던 멤버함수 처럼 사용할 수 있다.
그런데 확장함수가 public이고 그 내부에서 수신객체 클래스의 private 함수를 가져온다면?
캡슐화가 깨진다.
- 따라서 확장함수는 클래스에 있는 private 또는 protected 멤버를 애초에 가져올 수 없다.
만약 멤버함수와 확장함수의 시그니처가 같다면?
1
2
3
4
5
6
7
public class Person {
public int nextYearAge() {
System.out.println("멤버 함수");
return this.age + 1;
}
//...
}
1
2
3
4
fun Person.nextYearAge(): Int {
println("확장 함수")
return this.age + 1
}
- 멤버함수가 우선적으로 호출된다.
- 따라서 확장 함수를 만들어 놨는데 후에 다른 기능의 같은 멤버 함수가 생긴다면 멤버 함수에 우선순위가 생기기 때문에 예기치 않은 오류가 발생할 수 있어 주의해야 한다.
확장 함수가 오버라이드 된다면?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
open class Train(
val name: String = "새마을기차",
val price: Int = 5_000,
)
fun Train.isExpensive(): Boolean {
println("Train 확장함수")
return this.price >= 10000
}
class Srt : Train("SRT", 40_000)
fun Srt.isExpensive(): Boolean {
println("Srt 확장함수")
return this.price >= 20000
}
이 경우 어떤 것이 우선순위를 가질까?
1
2
3
4
5
6
val train: Train = Train()
train.isExpensive() // Train의 확장함수
val srt1: Train = Srt() // Train의 확장함수
val srt2: Srt = Srt() // Srt의 확장함수
- 즉 변수를 선언한 타입에 따라 해당 확장 함수가 호출되는 것을 알 수 있다.
- 어떤 객체인지는 중요하지 않다.
- 해당 변수의 현재 타입이 우선순위를 갖는다.
infix 함수 (중위 함수)
함수를 호출하는 새로운 방법이다.
이전 포스트에서의 downTo, step과 같은 함수이다.
- 변수.method(argument) 대신 변수 method argument 의 형식으로 사용한다.
1
2
3
4
5
6
7
fun Int.add(other: Int): Int {
return this + other
} //확장 함수
infix fun Int.add2(other: Int): Int {
return this + other
}
1
2
3
4
5
6
7
//사용
3.add(4) // 확장 함수
3.add2(4) // 가능
3 add2 4 // 중위 함수의 활용
inline 함수
함수가 호출되는 대신, 함수를 호출한 지점에 함수 본문을 그대로 복사 붙여넣기 하고 싶은 경우에 사용한다.
예시를 보자.
- 함수를 파라미터로 전달할 때의 함수를 계속 중첩해서 쓰는 경우 함수 call chain에 오버헤드가 생겨 inline 함수를 사용하게 되는데 이 때오버헤드를 줄일 수 있다.
- 그러나 inline 함수의 사용은 성능 측정과 함께 신중하게 사용되어야 한다.
지역 함수
함수 안에 함수를 선언할 수 있는 것을 뜻한다.
1
2
3
4
5
6
7
8
9
10
11
fun createPerson(firstName: String, lastName: String): Person {
if (firstName.isEmpty()) {
throw IllegalArgumentException()
}
if (lastName.isEmpty()) {
throw IllegalArgumentException()
}
return Person(firstName, lastName, 1)
}
- 조건문이 중복된 부분을 줄이고 싶게 생겼다.
- 이 때 지역 함수를 활용할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
fun createPerson(firstName: String, lastName: String): Person {
fun validateName(name: String, fieldName: String) {
if (name.isEmpty()) {
throw IllegalArgumentException()
}
}
validateName(firstName, "firstName")
validateName(lastName, "lastName")
return Person(firstName, lastName, 1)
}
- 메서드 내부에 메서드를 만들어 활용했다.
- 함수로 추출하면 좋을 것 같은 부분이지만 추출한 메서드가 현재 함수 내에서만 사용될 것 같을 때 사용할 수 있다.
그런데 depth가 깊어지기도 하고 코드가 그렇게 깔끔하지 않다는 것을 느낄 수 있다.
따라서 잘 사용되지 않는다. 오히려 Person 클래스와 같은 곳에서 저러한 검증 메서드를 만들어주는 것이 더 깔끔하다.
정리
- Java 코드가 있는 상황에서 Kotlin 코드로 추가 기능 개발을 하기 위해 확장 함수와 확장 프로퍼티가 등장했다.
- 확장 함수는 원본 클래스의 private, protected 멤버 접근이 되지 않는다.
- 멤버함수와 확장함수 중 멤버함수에 우선권이 부여된다.
- 확장함수는 현재 타입을 기준으로 호출된다.
- Java에서는 static 함수를 쓰는 것 처럼 Kotlin의 확장함수를 사용할 수 있다.
- 함수 호출 방식을 바꿔주는 infix 함수가 존재한다.
- 함수를 복사 - 붙여넣기 하는 inline 함수가 존재한다.
- Kotlin에서는 함수 내부에 함수를 선언할 수 있고 이를 지역함수라고 부른다.