Prologue

코틀린도 자바와 마찬가지로 가변인자를 지원하는데, 가변인자에 대해 알아보자.

Subject

[Usage]

  • 가변 인자를 사용하면 함수를 호출할 때, 인자의 갯수를 유동적으로 지정할 수 있다.
  • 사용법은 간단하며, 인자 앞에 vararg를 붙이면 된다.
1
2
3
4
5
6
7
8
9
10
11

/**
* created by victory_woo on 2020/10/27
* */

fun main(args: Array<String>) {
println(sum(1)) // 1
println(sum(1, 2, 3, 4, 5)) // 15
}

fun sum(vararg num: Int) = num.sum()

[배열을 가변인자로 넘기기]

  • 이미 존재하는 배열을 함수의 가변인자(vararg)로 넘겨야 할 때가 있다. 이 경우, 배열 이름 앞에 *를 붙여주면 된다.
  • ‘*’ : spread operator라고 부른다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* created by victory_woo on 2020/10/27
* */

fun main(args: Array<String>) {
val array = arrayOf("Lee", "Park")
print(array) // 1
print(*array) // 2

}

fun print(vararg s: String) {
println(s.joinToString("/"))
}

1번의 경우에는 정확한 결과를 출력할 수 없다. 2번처럼 *를 배열 앞에 붙여줘야 "Lee/Park"와 같은 결과를 얻을 수 있다. 왜 이런 결과가 나오는지 바이트 코드를 통해 확인해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import kotlin.Metadata;
import kotlin.TypeCastException;
import kotlin.collections.ArraysKt;
import kotlin.jvm.functions.Function1;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;

public final class VarargSampleKt {
public static final void main(@NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, "args");
String[] arr = new String[]{"Lee", "Park"};
printWoo((String)arr);
printWoo((String[])Arrays.copyOf(arr, arr.length));
}
}

@NotNull
public static final String[] convert(@NotNull String... s) {
Intrinsics.checkParameterIsNotNull(s, "s");
return s;
}

public static final void printWoo(@NotNull String... s) {
Intrinsics.checkParameterIsNotNull(s, "s");
String var1 = ArraysKt.joinToString$default(s, (CharSequence)"/", (CharSequence)null, (CharSequence)null, 0, (CharSequence)null, (Function1)null, 62, (Object)null);
System.out.println(var1);
System.out.println();
}
}

가변 인자를 받은 print() 함수의 경우, 컴파일 타임에 String을 받는 것으로 인식이 된다. 따라서 array를 가변 인자로 넘기는 경우에는 Array이 String을 인자로 받는 print() 함수에 들어갈 수 없다는 것이다. 바이트 코드를 통해 보면, (String)arr

반면 *array를 가변 인자로 넘기는 경우, 바이트 코드를 통해 확인해보면 Arrays.copyOf(array, array.length)를 통해 복사한 배열을 String[]로 캐스팅하여 인자로 넘기는 것을 볼 수 있다. *(spread operator)를 명시하여 printWoo() 함수가 받는 인자가 가변 인자라는 것을 알려주어 String[]을 받을 수 있도록 해주는 것으로 생각이 된다.

printWoo(arr)의 경우에는 가변 인자라는 것을 명시해주지 않았기 때문에 printWoo() 함수는 String 인자를 받게 되어 있고, arr은 Array이기 때문에 타입이 맞지 않아 컴파일 에러가 발생하는 것으로 생각이 된다. 이를 피하고 싶다면 printWoo("Lee", "Park") 처럼 가변 인자를 명시적으로 기입하면 가능하다.

만약, vararg 파라미터가 제네릭 타입일 경우, *을 생략해도 Array 타입은 인자로 받아들일 수 있다. 코틀린의 공변성 개념으로, 자기 자신과 자식 객체만을 허용한다는 의미이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import kotlin.collections.ArrayList

/**
* created by victory_woo on 2020/10/27
* */

fun main(args: Array<String>) {
val array = arrayOf(1, 2, 3)
asList(array).forEach {
for (num in it) print("$num ")
println()
}
// 1,2,3

asList(-1, 0, *array, 4).forEach {
print("$it ")
}
println()
// -1,0,1,2,3,4
}

fun <T> asList(vararg ts: T): List<T> {
return ArrayList<T>().apply {
ts.forEach { add(it) }
}
}

Ref