1. ๊ฐ๋ณ์ธ์ & ์ ๋ค๋ฆญ ๐ช
๊ฐ๋ณ์ธ์(varargs)๋ ๋งค๊ฐ๋ณ์๋ก ๋ค์ด์ค๋ ๊ฐ์ ๊ฐ์์ ์๊ด์์ด ๋์ ์ผ๋ก ์ธ์๋ฅผ ๋ฐ์์ ๊ธฐ๋ฅํ๋๋ก ํด์ฃผ๋ ๋ฌธ๋ฒ์ ๋งํ๋ค.
public PrintStream printf(String format, Object ... args) {
return format(format, args);
}
๊ฐ๋ณ์ธ์ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด ๊ฐ๋ณ์ธ์๋ฅผ ๋ด๊ธฐ ์ํ ๋ฐฐ์ด์ด ์๋์ผ๋ก ํ๋ ๋ง๋ค์ด์ง๋ค.
๋ง์ฝ ๋ค์๊ณผ ๊ฐ์ด ์ ๋ค๋ฆญ๊ณผ ๊ฐ๋ณ์ธ์๋ฅผ ํจ๊ป ์ฌ์ฉํ๋ฉด ์ด๋ป๊ฒ ๋ ๊น?
static void dangerous(List<String>... stringLists) { }
์ด ๊ฒฝ์ฐ์๋ ๊ฐ๋ณ์ธ์ ๋ฉ์๋๊ฐ ํธ์ถ๋๊ธฐ ๋๋ฌธ์ ๊ฐ๋ณ์ธ์๋ฅผ ๋ด๊ธฐ ์ํ ๋ฐฐ์ด์ด ์๋์ผ๋ก ๋ง๋ค์ด์ง๋ค. ์ฆ, ์ ๋ค๋ฆญ ๋ฐฐ์ด์ด ๋ง๋ค์ด์ง๋ ๊ฒ์ด๋ค.
์ผ๋ฐ์ ์ผ๋ก๋ ์ ๋ค๋ฆญ ๋ฐฐ์ด์ ๋ง๋ค๋ ค๊ณ ํ ๋ ์ปดํ์ผ๋ฌ๊ฐ ์ปดํ์ผ ์๋ฌ๋ฅผ ๋ด๊ธฐ ๋๋ฌธ์ ์ฐ๋ฆฌ๊ฐ ์ง์ ์ ๋ค๋ฆญ ๋ฐฐ์ด์ ๋ง๋ค ์๋ ์๋ค.
public static void main(String[] args) {
// ์ปดํ์ผ ์๋ฌ
// Generic array creation
List<String>[] myList = new ArrayList<String>[10];
}
ํ์ง๋ง ๊ฐ๋ณ์ธ์๋ผ๋ ๊ฒ์ ์ฌ์ฉํ๋ฉด ์๋ฐ๊ฐ ๋ด๋ถ์ ์ผ๋ก ์ ๋ค๋ฆญ์ ๋ฐฐ์ด์ ๋ง๋ค๊ฒ ๋๋ค. ๋ฌผ๋ก , ์ปดํ์ผ ์๋ฌ๋ ์๋์ง๋ง ๊ฒฝ๊ณ ๊ฐ ๋จ๊ธด ํ๋ค.
// ๊ฒฝ๊ณ ๋ฐ์
// Possible heap pollution from parameterized vararg type
static void dangerous(List<String>... stringLists) { }
์์ดํ 28์์ ์ดํด๋ณธ ๊ฒ์ฒ๋ผ ๋ง์ฝ ์ ๋ค๋ฆญ ๋ฐฐ์ด์ ํ์ฉํ๋ฉด ํ์ ์์ ์ฑ์ ๋ฌธ์ ๊ฐ ์๊ธด๋ค. ๊ทธ๋์ ์ปดํ์ผ๋ฌ๊ฐ ์ ๋ค๋ฆญ ๋ฐฐ์ด์ ์์ ์์ฑํ์ง ๋ชปํ๊ฒ ๋ง์๋๋ฐ, ์ง๊ธ์ฒ๋ผ ๊ฐ๋ณ์ธ์๋ฅผ ์ฌ์ฉํ๋ฉด ์ ๋ค๋ฆญ ๋ฐฐ์ด์ ํ์ฉํ๊ฒ ๋์ด์ ์๋นํ ์ํํ ์ฝ๋๊ฐ ๋๊ฒ ๋๋ค.
๊ทธ๋ฌ๋ฉด '์ ๋ค๋ฆญ๊ณผ ๊ฐ๋ณ์ธ์๋ฅผ ํจ๊ป ์ฐ์ง ๋ชปํ๋๋ก ๋ง์๋๋๋ฐ ๋ง์ง ์์๊น?'๋ผ๋ ์๋ฌธ์ ํ์ ์ ์๋ค. ํ์ง๋ง, ์ ๋ค๋ฆญ๊ณผ ๋งค๊ฐ๋ณ์ํ ํ์ ์ varargs ๋งค๊ฐ๋ณ์๋ฅผ ๋ฐ๋ ๋ฉ์๋๊ฐ ์ค๋ฌด์์ ๋งค์ฐ ์ ์ฉํ๊ธฐ ๋๋ฌธ์ ํ์ฉ์ ํด๋์ ๊ฒ์ด๋ค.
์ธ์ด ์ค๊ณ์๊ฐ ์ด ๋ชจ์์ ์์ฉํ๊ธฐ๋ก ํ ๊ฒ์ด๋ค. ์๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์๋ ์ด๋ฐ ๋ฉ์๋๋ฅผ ์ฌ๋ฟ ์ ๊ณตํ๊ณ ์๋ค.
Arrays.asList(T... a)
Collections.addAll(Collection<? super T> c, T... elements)
EnumSet.of(E first, E... rest)
์ ๋ค๋ฆญ varargs ๋ฉ์๋๋ฅผ ๋ค์ ๋ ์กฐ๊ฑด์ ๋ชจ๋ ๋ง์กฑํด์ผ ์์ ํ๋ค.
- varargs ๋งค๊ฐ๋ณ์ ๋ฐฐ์ด์ ์๋ฌด๊ฒ๋ ์ ์ฅํ์ง ์๋๋ค.
- ๊ทธ ๋ฐฐ์ด(ํน์ ๋ณต์ ๋ณธ)์ ์ ๋ขฐํ ์ ์๋ ์ฝ๋์ ๋ ธ์ถํ์ง ์๋๋ค.
๋ ๊ฐ์ ์กฐ๊ฑด์ ๋ํด์ ๊ตฌ์ฒด์ ์ผ๋ก ์์๋ณด์.
2. ์กฐ๊ฑด (1). varargs ๋งค๊ฐ๋ณ์ ๋ฐฐ์ด์ ์๋ฌด๊ฒ๋ ์ ์ฅํ์ง ์๋๋ค. ๐ฆพ
varargs ๋งค๊ฐ๋ณ์ ๋ฐฐ์ด์ ๋ฌด์์ธ๊ฐ๋ฅผ ์ ์ฅํ๋ฉด varargs ๋ฉ์๋๊ฐ ์์ ํด์ง์ง ์๊ฒ ๋๋ค.
static void dangerous(List<String>... stringLists) {
List<Integer> intList = List.of(10);
Object[] objects = stringLists;
objects[0] = intList; // ํ ์ค์ผ ๋ฐ์
String s = stringLists[0].get(0); // ClassCaseException
}
๋ฐฐ์ด์ ๊ณต๋ณ์ด๋ฏ๋ก ๋ฌธ์์ด ๋ฐฐ์ด์ ์ค๋ธ์ ํธ ๋ฐฐ์ด์ ํ ๋นํ ์ ์๋ค. ๊ทธ๋ฆฌ๊ณ ์ปดํ์ผ๋ฌ๊ฐ ์ ๋ค๋ฆญ ํ์ ์ ์๊ณ ์์ผ๋ฏ๋ก, String s= stringLists[0].get(0);๋ฅผ ์ปดํ์ผํ ๋ ์๋ฐ๊ฐ String์ผ๋ก ์บ์คํ ์ ํ๋ ์ฝ๋๋ฅผ ๋ฃ์ด์ค๋ค. ์ด๋ Integer๋ฅผ String์ผ๋ก ์บ์คํ ํ๋ ๊ฒ์ด๋ฏ๋ก ๋ฌธ์ ๊ฐ ์๊ธฐ๊ฒ ๋๋ค.
์ ๋ค๋ฆญ์ ์ด ๊ฐ์ฅ ํฐ ์ด์ ๋ ์ปดํ์ผ ํ์๋ถํฐ ๋ฐํ์๊น์ง ํ์ ์์ ์ฑ์ ํ๋ณดํ๊ธฐ ์ํด์๋ค. ๊ทธ๋ฐ๋ฐ ์์ ๊ฐ์ด ๋ฐํ์์ ํ์ ์์ ์ฑ์ด ๊นจ์ง๋ ๋ฌธ์ ๊ฐ ์๊ธด๋ค. ๋ฌผ๋ก ์ปดํ์ผ ํ์์๋ ํ์ธ์ด ๋์ง ์๋๋ค.
ํ์ง๋ง, ์๋ ์ฝ๋์ ๊ฐ์ด varargs ๋งค๊ฐ๋ณ์ ๋ฐฐ์ด์ ์๋ฌด๊ฒ๋ ์ ์ฅํ์ง ์์ผ๋ฉด ์ ๋ค๋ฆญ varargs ๋ฉ์๋๋ ์์ ํ๋ค.
@SafeVarargs
static <T> List<T> flatten(List<? extends T>... lists) {
List<T> result = new ArrayList<>();
for(List<? extends T> list : lists)
result.addAll(list);
return result;
}
์์ ํ๊ฒ ์ฐ๋ ๊ฒฝ์ฐ์ @SuppressWarnings๋ฅผ ์ฌ์ฉํ ์ ์๋ค. ํ์ง๋ง ์ด๋ ๊ฒ ํ๋ฉด ๋ฉ์๋ ์ ์ฒด๊ฐ @SuppressWarings์ ์ํฅ์ ๋ฐ๊ฒ ๋๋ค.
์ด๋ฐ ๊ฒฝ์ฐ @SafeVarargs๋ผ๋ ๊ฒ์ ์ฌ์ฉํ๋ฉด ๊ฐ๋ณ์ธ์๋ ์์ ํ๊ฒ ์ฌ์ฉ๋๊ณ ์๋ค๊ณ ํ์ ํ ์ ์๋ค. ์ฆ, ๊ฐ๋ณ์ธ์ ์ด์ธ์ ๊ฒฝ๊ณ ์ ๋ํด์๋ ์ฒดํฌ๋ฅผ ํ ์ ์๋ค๋ ๊ฒ์ด๋ค.
3. ์กฐ๊ฑด (2). ๊ทธ ๋ฐฐ์ด(ํน์ ๋ณต์ ๋ณธ)์ ์ ๋ขฐํ ์ ์๋ ์ฝ๋์ ๋ ธ์ถํ์ง ์๋๋ค. ๐ฆต
์๋์ ์ฝ๋๋ ๊ฐ๋ณ์ธ์๋ก ๋์ด์จ ๋งค๊ฐ๋ณ์๋ค์ ๋ฐฐ์ด์ ๋ด์ ๋ฐํํ๋ ์ ๋ค๋ฆญ ๋ฉ์๋๋ค.
static <T> T[] toArray(T... args) {
return args;
}
์ปดํ์ผ๋ฌ๋ toArray์ return ํ์ ์ Object๋ผ๊ณ ํ๋จํ๋ค. Object๊ฐ ๊ฐ์ฅ ์ถ์์ ์ด๊ธฐ ๋๋ฌธ์ด๋ค. ๊ฒฐ๊ณผ์ ์ผ๋ก return ํ๋ ๊ฒ์ Object์ ๋ฐฐ์ด์ด๋ค.
toArray๋ฅผ ๋ค์๊ณผ ๊ฐ์ ์ํฉ์์ ์ด๋ค๊ณ ์๊ฐํด ๋ณด์.
public class PickTwo {
static <T> T[] toArray(T... args) {
return args;
}
static <T> T[] pickTwo(T a, T b, T c) {
switch(ThreadLocalRandom.current().nextInt(3)) {
case 0: return toArray(a, b);
case 1: return toArray(a, c);
case 2: return toArray(b, c);
}
throw new AssertionError(); // ๋๋ฌํ ์ ์์
}
}
----------
public static void main(String[] args) {
String[] attributes = pickTwo("great", "best", "good"); // ClassCastException
System.out.println(Arrays.toString(attributes));
}
pickTwo๋ฅผ ํ ๋์๋ ์ ๋ค๋ฆญ์ ์ฌ์ฉํ์๊ณ , String์ผ๋ก ํ์ ์ ์ค์ ํ์๊ธฐ ๋๋ฌธ์ ์ปดํ์ผ๋ฌ๋ String์ผ๋ก ํ์ ์บ์คํ ์ ํ๋ ค๊ณ ํ๋ค.
toArray์์ Object๋ฅผ return ํด์ฃผ์๊ณ , ์ด๊ฒ์ ๋ค์ pickTwo์์ return ํ๋ ๊ฒ์ด๋ฏ๋ก ํ์ฌ๋ Object ํ์ ์ด๋ค. ๊ทธ๋ฌ๋ฏ๋ก Object๋ฅผ String์ผ๋ก ํ์ ์บ์คํ ์ ํ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ๋น์ฐํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
์ฆ, ๊ทธ ๋ฐฐ์ด(ํน์ ๋ณต์ ๋ณธ)์ ์ ๋ขฐํ ์ ์๋ ์ฝ๋์ ๋ ธ์ถํ์ง ์์์ผ์ง ์ ๋ค๋ฆญ varargs ๋ฉ์๋๋ฅผ ์์ ํ๊ฒ ์ฌ์ฉํ ์ ์๋ค.
4. ๋ค๋ฅธ ํด๊ฒฐ์ฑ - varargs ๋งค๊ฐ๋ณ์๋ฅผ List ๋งค๊ฐ๋ณ์๋ก ๋ฐ๊ฟ๋ผ. ๐ฆฟ
์ ๋ค๋ฆญ varargs ๋ฉ์๋๋ฅผ ์์ ํ๊ฒ ๋ง๋ค๊ณ @SafeVarargs ์ ๋ํ ์ด์ ์ ๋ค๋ ๊ฒ ์ ์ผํ ์ ๋ต์ ์๋๋ค.
์์ดํ 28(๋ฐฐ์ด๋ณด๋ค๋ ๋ฆฌ์คํธ๋ฅผ ์ฌ์ฉํ๋ผ)์ ์กฐ์ธ์ ๋ฐ๋ผ ์ค์ฒด๋ ๋ฐฐ์ด์ธ varargs ๋งค๊ฐ๋ณ์๋ฅผ List ๋งค๊ฐ๋ณ์๋ก ๋ฐ๊ฟ ์๋ ์๋ค.
// ๊ธฐ์กด ์ฝ๋
public class FlattenWithVarargs {
@SafeVarargs
static <T> List<T> flatten(List<? extends T>... lists) {
List<T> result = new ArrayList<>();
for(List<? extends T> list : lists)
result.addAll(list);
return result;
}
}
----------
public static void main(String[] args) {
List<Integer> flatList = flatten(List.of(1, 2), List.of(3, 4, 5), List.of(6, 7));
System.out.println(flatList); // [1, 2, 3, 4, 5, 6, 7]
}
// ๋ณ๊ฒฝ๋ ์ฝ๋
public class FlattenWithList {
static <T> List<T> flatten(List<List<? extends T>> lists) {
List<T> result = new ArrayList<>();
for(List<? extends T> list : lists)
result.addAll(list);
return result;
}
}
----------
public static void main(String[] args) {
List<Integer> flatList = flatten(List.of(
List.of(1, 2), List.of(3, 4, 5), List.of(6, 7)
));
System.out.println(flatList); // [1, 2, 3, 4, 5, 6, 7]
}
์ด ๋ฐฉ์์ ์ฅ์ ์ ์ปดํ์ผ๋ฌ๊ฐ ์ด ๋ฉ์๋์ ํ์ ์์ ์ฑ์ ๊ฒ์ฆํ ์ ์๋ค๋ ๋ฐ ์๋ค. @SafeVarargs ์ ๋ํ ์ด์ ์ ์ฐ๋ฆฌ๊ฐ ์ง์ ๋ฌ์ง ์์๋ ๋๋ฉฐ, ์ค์๋ก ์์ ํ๋ค๊ณ ํ๋จํ ๊ฑฑ์ ๋ ์๋ค.
๋จ์ ์ด๋ผ๋ฉด ํด๋ผ์ด์ธํธ ์ฝ๋๊ฐ ์ด์ง ์ง์ ๋ถํด์ง๊ณ ์๋๊ฐ ์กฐ๊ธ ๋๋ ค์ง ์ ์๋ค๋ ์ ๋์ด๋ค.
๋ํ, pickTwo์๋ ์ ์ฉ์ด ๊ฐ๋ฅํ๋ค.
// ๊ธฐ์กด ์ฝ๋
public class PickTwo {
static <T> T[] toArray(T... args) {
return args;
}
static <T> T[] pickTwo(T a, T b, T c) {
switch(ThreadLocalRandom.current().nextInt(3)) {
case 0: return toArray(a, b);
case 1: return toArray(a, c);
case 2: return toArray(b, c);
}
throw new AssertionError(); // ๋๋ฌํ ์ ์์
}
}
----------
public static void main(String[] args) {
String[] attributes = pickTwo("great", "best", "good"); // ClassCastException
System.out.println(Arrays.toString(attributes));
}
// ๋ณ๊ฒฝ๋ ์ฝ๋
public class SafePickTwo {
static <T> List<T> pickTwo(T a, T b, T c) {
switch (ThreadLocalRandom.current().nextInt(3)) {
case 0: return List.of(a, b);
case 1: return List.of(a, c);
case 2: return List.of(b, c);
}
throw new AssertionError();
}
}
----------
public static void main(String[] args) {
List<String> attributes = pickTwo("great", "best", "good");
System.out.println(attributes);
}
ํด๋น ๊ธ์ ๋ฐฑ๊ธฐ์ ๋์ '์ดํํฐ๋ธ ์๋ฐ ์๋ฒฝ ๊ณต๋ต'์ ์ฐธ๊ณ ํ์์ต๋๋ค.