중위 표기법(중위 함수)-Infix
1. Infix?
함수(메서드)는 메서드인데 일반적이지 않은 형태로 사용할 수 있는 함수이다. 확장함수의 일종으로 가독성이 훨씬 좋아질 수 있다. is, until, and, or, ...
모두 코틀린에서 만들어 둔 중위함수, infix 이다. (참고: until source code)
fun standardInfixTest() {
val str = "hello"
val num = 10
val arr = arrayOf(1, 2, 3, 4)
if((str is String) and (num is Int)) { //infix: is, and
for((idx, arrNum) in arr.withIndex()) { //infix: in
println("hello arr[$idx]: ${(arrNum+num)}")
}
}
}
2. Infix 함수 만들기
infix 함수의 정의 형태는 infix fun R.functionName(t: T): V
이다. 이 함수를 정의하기 위해서는,
- 리시버 객체가 꼭 있어야 한다. (R이 있어야 한다.)
- 파라미터 객체가 꼭 1개만 있어야 한다. (T가 있어야 한다.)
- 파라미터 객체가 vararg면 안된다.
- 파라미터 객체는 default 값을 설정할 수 없다.
를 지켜야 한다.
생각을 해보면 중위 함수는object1 is ObjectClass
나something1 and something2
처럼 '중간'에 들어가는 것이니 왼쪽과 오른쪽에 모두 하나씩만 꼭 존재해야 말이 되지 않나.
2-1. vararg가 안된다는 것이지, 컬렉션이 안된다는 것은 아니다.
infix fun Int.plusToEachOf(arr: IntArray): IntArray {
return arr.map { it + this }.toIntArray()
}
fun test() {
val result: IntArray = 10 plusToEachOf intArrayOf(0, 1, 2, 3, 4, 5)
println("result: ${result.contentToString()}")
}
2-2. 클래스 내에 선언하면 리시버 함수가 없어도 된다.
infix notation, kotlinlang.org
class InfixJoinWithDashClass {
infix fun joinWithDash(str: String): String {
return "$this-$str"
}
infix fun String.joinWithDashWithR(str: String): String {
return "$this-$str"
}
fun build() {
println(joinWithDash("is this object")) // infix.InfixJoinWithDashClass@4a574795-is this object
println("this is" joinWithDashWithR "joinWithDashWithR()") //joinWithDashForOuter-use this
}
}
위 처럼 클래스 안에 두가지 infix 함수가 선언되었다.
하나는 리시버가 없고(없는 것 같이 보이고) 하나는 리시버가 있다.
둘 다 쓸 수 있을까? 둘 다 클래스 내에서든, 클래스를 인스턴스화 해서든 쓸 수 있을까?
둘 다 클래스 내에서는 사용가능하다. But 리시버가 있는 함수는 클래스 밖에서 사용할 수 없다.
왜그럴까?
생각해보면 중위함수는 확장함수의 형태이고 동작도 확장함수처럼 동작한다.
- 그럼 첫번째, 함수 정의를 위한 조건에서 리시버가 반드시 있어야 한다고 했는데?
- 클래스 자체(this)를 리시버로 가정한다.
this.joinWithDash()
가 되는 것이다. - 중위함수는 확장함수와 아주 유사하다고 했다. 확장함수는 클래스 밖에서 클래스를 확장하는 역할을 한다. 그러므로 중위함수를 이러한 맥락에서 생각해보면 this 라는 리시버를 붙인다고 생각할 수 있다.
- 클래스 자체(this)를 리시버로 가정한다.
- 리시버가 있는 함수와 없는 함수가 클래스 밖에서는 왜 쓰일 수 있고 쓰일 수 없지?
- 위에서 어느정도 설명이 된 것 같다. 확장함수의 소속은 리시버 클래스이다. joinWithDashWithR 메서드의 소속은 String이다. (확장함수는 static 이다.) 그런데 InfixJoinWithDashClass 클래스 안에 있기 때문에 밖으로 내보낼 때에 어떤 소속으로 내보내야 할 것인가가 불분명해진다. 때문에 함수의 사용 범위가 제한되게 되는 것이다.
- 추가적으로 joinWithDashWithR 메서드를 InfixJoinWithDashClass의 companion object 안에 넣으면 클래스 밖에서 사용할 수 있게된다. (나의 논리가 맞는것인지 확인을 위해 테스트 해 본 것일 뿐, 이렇게까지 사용할 사람은 없을거라고 생각하자.)
fun test() {
val infixJoinWithDashClass = InfixJoinWithDashClass()
val strWithJoinWithDashMethod = infixJoinWithDashClass joinWithDash "is object of InfixJoinWithDashClass."
val strWithJoinWithDashWithRMethod = "This is made by" joinWithDashWithR "joinWithDashWithR method." //Error! lint에서부터 잡는 에러.
}