[Kotlin Deep Dive] 2. 확장 함수, 확장 프로퍼티, 중위 호출

akim·2022년 11월 5일
0

Kotlin Deep Dive

목록 보기
2/9
post-thumbnail

1. 확장 함수

Kotlin in Action 책에서는 확장 함수를 이렇게 정의하고 있다.

확장 함수는 어떤 클래스의 멤버 메서드인 것 처럼 호출할 수 있지만 그 클래스의 밖에 선언된 함수다.

이렇게 정의된 문장은... 거부감이
좀 더 쉽게 생각해보자.

왜 클래스의 밖에 선언되어 있을까?

바로 확장 함수이기 때문이다. (이게 무슨..)

예를 들어 기본 Java 컬렉션에 String이라는 클래스가 있는데 나는 이 String에 딱 한 가지 기능이 더 있으면 좋겠다는 생각이 들었다고 하자. 즉, String의 기능을 내 맘대로 확장해서 쓰고 싶은거다.

그러나 내 마음대로 이 String 클래스를 고칠수는 없다. 이 클래스는 내가 직접 작성한 코드도 아니고, 그 클래스의 소스코드를 소유한 것도 아니기 때문이다.

이럴 때 이 String 클래스의 직접적인 수정 없이도 바깥에 새로운 메서드를 추가해서 기능을 더할 수 있는 게 바로 확장 함수다. 말 그대로 기존의 기능을 확장한 함수다.

그럼 어떻게 기능을 확장할 수 있을까?

package strings
fun String.lastChar(): Char = this.get(this.length-1)

println("Kotlin".lastChar())

위는 Kotlin in Action에 나오는 예제 코드다.

보면 String이라는 기본 클래스에 lastChar라는 말이 붙었다. 즉, 나는 String클래스에서 마지막 글자를 구하는 하나의 확장된 기능을 필요로 하는 것이다.

확장 함수를 쓰고 싶다면 위와 같이 확장할 클래스 뒤에 추가하려는 함수를 써주면 된다.

이때 확장할 클래스를 수신 객체 타입(receiver type)이라 하고, 위 예시의 경우 String이 이에 해당한다.
또, 이 확장 함수를 만들었으니 분명 어떤 객체에 대해서 쓰게 될 텐데, 그 객체를 바로 수신 객체(receiver object)라고 한다.

이쯤 되어 다시 정의문을 살펴보자.

확장 함수는 어떤 클래스의 멤버 메서드인 것 처럼 호출할 수 있지만 그 클래스의 밖에 선언된 함수다.

어떤 클래스를 확장한 형태이므로 그 클래스의 멤버 메서드인 것 처럼 호출이 가능한 형태이지만, 직접 내가 그 클래스에 접근하여 수정하거나 추가한 것이 아닌 외부에 선언한 함수라는 말이다. 이제 어느 정도 확장 함수에 대한 감이 잡힌다.

2. 확장 프로퍼티

그렇다면 확장 프로퍼티는 무엇일까?

확장 함수가 기존 클래스의 기능을 확장한 함수였으니 확장 프로퍼티는 기존 클래스의 기능을 확장한 프로퍼티가 아닌가 하는 생각이 든다.

맞다!

확장 프로퍼티를 사용하면 기존 클래스 객체에 대한 프로퍼티 형식의 구문으로 사용할 수 있는 API를 추가할 수 있다.

여기서 논란의 키워드였던 API .... 우리는 위 문장을 이렇게 해석해보았다.

확장 프로퍼티를 사용하면 기존 클래스 객체에 프로퍼티 형식으로 새 기능을 추가할 수 있다.

조금 더 clear한 설명이 되었는가? 그렇기를 바란다....


앞선 KDD 1편에서 살펴본 바로는 프로퍼티는 해당 클래스가 가지고 있는 일종의 속성이 된다고 했다.

그러나 확장 프로퍼티의 경우 기존 클래스에 작성된 멤버가 아니고, 내가 바깥에 추가로 작성하는 것이기에 프로퍼티라는 이름으로 불리기는 하지만 상태를 저장할 적절한 방법이 없다.

즉, 실제로 확장 프로퍼티는 아무 상태도 가질 수 없게 된다.


아무 상태도 가질 수 없는 프로퍼티가 도대체 무슨 소용인가?

소용이 있다.

앞서 살펴본 확장 함수의 구문을 확장 프로퍼티를 이용해 더 짧게 작성할 수 있어서 편한 경우들이 있다.
아까의 lastChar라는 함수를 프로퍼티로 바꿔보자.

val String.lastChar: Char
		get() = get(length -1)

일반 프로퍼티와 비슷한 형태를 보인다. 달라진 점은 수신 객체 클래스String이 추가되었다는 것이다.
아까 쓴 확장 함수보다 확실히 짧아졌지만 같은 기능을 할 수 있다. 이래서 확장 프로퍼티를 쓰는 것이다.

또한 게터가 정의된 것을 볼 수 있는데, 클래스 안에 쓰인 멤버가 아니기에 뒷받침해주는 필드가 없으므로 게터는 꼭 정의를 해줘야 한다.

3. 중위 호출

Kotlin으로 코드 좀 짜본 사람들이라면 map을 이용하면서 1 to "one"과 같은 코드들을 자주 써봤을 것이다.
중위 호출에서 다루고자 하는 것은 이 to에 대한 얘기다.

이전까지 나는 아무 생각없이 to를 써왔다. 그냥 map에 key와 value로 쌍을 지정해 줄 땐 to를 써주는 거구나~ 하고 말았던 것 같다.

to를 쓰는지, to가 키워드인지 함수인지, 함수라면 도대체 왜 얘는 형식이 이렇게 다른지 등등..
조금만 생각해봐도 이상한 부분이 한 두개가 아닌 놈이었는데 이걸 놓치다니..

오늘에서야 그 정체를 알게 되었다. to함수였다!

함수인데 형태가 왜 이런가요?

바로 중위 호출을 했기 때문이다. (데자뷰)

중위 호출은 일반적인 함수 호출과 달리 수신 객체메서드 인자 사이에 메서드 이름을 넣어 호출하는 형태를 말한다.

이렇게 보면 역시나 이해가 안 된다. 예시를 보자.

1.to("one") // "to" 메소드를 일반적인 방식으로 호출
1 to "one" // "to" 메소드를 중위 호출 방식으로 호출

to 함수는 타입과 관계없이 임의의 순서쌍을 만들어주는 함수다.
여기서 수신 객체는 1, 메서드 인자는 "one", 메서드 이름은 to 가 된다.

그러니까, 일반적으로 이 to 함수를 호출했다면 1.to("one")과 같이 적었을텐데, 이걸 중위 호출을 이용해 간단하게 1 to "one" 으로 작성할 수 있게 된 것이다.

이렇게 중위 호출을 함으로써 인자가 하나뿐인 함수를 보다 간편하고 보기 쉽게 사용할 수 있게 되었다.

profile
학교 다니는 개발자

0개의 댓글