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)
라고 한다.
이쯤 되어 다시 정의문을 살펴보자.
확장 함수는 어떤 클래스의 멤버 메서드인 것 처럼 호출할 수 있지만 그 클래스의 밖에 선언된 함수다.
어떤 클래스를 확장한 형태이므로 그 클래스의 멤버 메서드인 것 처럼 호출이 가능한 형태이지만, 직접 내가 그 클래스에 접근하여 수정하거나 추가한 것이 아닌 외부에 선언한 함수라는 말이다. 이제 어느 정도 확장 함수에 대한 감이 잡힌다.
그렇다면 확장 프로퍼티는 무엇일까?
확장 함수가 기존 클래스의 기능을 확장한 함수였으니 확장 프로퍼티는 기존 클래스의 기능을 확장한 프로퍼티가 아닌가 하는 생각이 든다.
맞다!
확장 프로퍼티를 사용하면 기존 클래스 객체에 대한
프로퍼티
형식의 구문으로 사용할 수 있는 API를 추가할 수 있다.
여기서 논란의 키워드였던 API
.... 우리는 위 문장을 이렇게 해석해보았다.
확장 프로퍼티를 사용하면 기존 클래스 객체에
프로퍼티
형식으로 새 기능을 추가할 수 있다.
조금 더 clear한 설명이 되었는가? 그렇기를 바란다....
앞선 KDD 1편에서 살펴본 바로는 프로퍼티는 해당 클래스가 가지고 있는 일종의 속성이 된다고 했다.
그러나 확장 프로퍼티의 경우 기존 클래스에 작성된 멤버가 아니고, 내가 바깥에 추가로 작성하는 것이기에 프로퍼티라는 이름으로 불리기는 하지만 상태를 저장할 적절한 방법이 없다.
즉, 실제로 확장 프로퍼티는 아무 상태도 가질 수 없게 된다.
소용이 있다.
앞서 살펴본 확장 함수의 구문을 확장 프로퍼티를 이용해 더 짧게 작성할 수 있어서 편한 경우들이 있다.
아까의 lastChar
라는 함수를 프로퍼티로 바꿔보자.
val String.lastChar: Char
get() = get(length -1)
일반 프로퍼티와 비슷한 형태를 보인다. 달라진 점은 수신 객체 클래스인 String
이 추가되었다는 것이다.
아까 쓴 확장 함수보다 확실히 짧아졌지만 같은 기능을 할 수 있다. 이래서 확장 프로퍼티를 쓰는 것이다.
또한 게터
가 정의된 것을 볼 수 있는데, 클래스 안에 쓰인 멤버가 아니기에 뒷받침해주는 필드가 없으므로 게터는 꼭 정의를 해줘야 한다.
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"
으로 작성할 수 있게 된 것이다.
이렇게 중위 호출을 함으로써 인자가 하나뿐인 함수를 보다 간편하고 보기 쉽게 사용할 수 있게 되었다.