API에서 와일드카드 사용
유연한 제네릭 메서드를 설계합니다
API에서 와일드카드 사용은(는) CoddyKit의 무료 Java Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Java Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
유연한 메서드 설계
와일드카드는 주로 API 설계자를 위한 도구입니다. 와일드카드를 적절히 배치하면 호출자가 형 변환 없이 더 다양한 형식을 전달할 수 있습니다.
목표는 호출자에게는 최대한의 유연성을 제공하고 내부에서는 완전한 형식 안전성을 유지하는 것입니다.
import java.util.List;
public class Main {
static double total(List<? extends Number> items) {
double sum = 0;
for (Number n : items) sum += n.doubleValue();
return sum;
}
public static void main(String[] args) {
System.out.println(total(List.of(1, 2, 3)));
System.out.println(total(List.of(1.5, 2.5)));
}
}제한 없는 와일드카드
List<?>는 제한 없는 와일드카드로, 정체를 알 수 없는 어떤 형식의 리스트를 의미합니다.
메서드가 요소 형식을 신경 쓰지 않을 때 사용합니다. 예를 들어 요소 개수를 세거나 비어 있는지 확인할 때 적합합니다.
import java.util.List;
public class Main {
static int size(List<?> any) {
return any.size();
}
public static void main(String[] args) {
System.out.println(size(List.of("a", "b")));
System.out.println(size(List.of(1, 2, 3)));
}
}List<?>와 List<Object>의 차이
두 형식은 서로 다릅니다. List<Object>는 Object로 선언된 리스트만 받습니다. List<?>는 어떤 형식의 리스트든 받습니다.
List<String>은 List<?> 매개변수에 전달할 수 있지만 List<Object> 매개변수에는 전달할 수 없습니다.
import java.util.List;
public class Main {
static void printAll(List<?> list) {
for (Object o : list) System.out.println(o);
}
public static void main(String[] args) {
List<String> names = List.of("Ada", "Linus");
printAll(names); // works; List<Object> would reject this
}
}형식 매개변수가 더 명확한 경우
요소 형식을 두 번 이상 참조해야 한다면 와일드카드보다 이름이 있는 형식을 사용하는 편이 읽기 좋습니다.
void swap(List<?> l, int i, int j)는 비공개 도우미가 필요하지만, 깔끔한 제네릭 버전과 비교해 볼 수 있습니다.
import java.util.ArrayList;
import java.util.List;
public class Main {
static <T> void swap(List<T> list, int i, int j) {
T tmp = list.get(i);
list.set(i, list.get(j));
list.set(j, tmp);
}
public static void main(String[] args) {
List<String> list = new ArrayList<>(List.of("a", "b", "c"));
swap(list, 0, 2);
System.out.println(list);
}
}도우미를 이용한 캡처 기법
공개 API에서 List<?>를 사용해야 한다면 비공개 제네릭 도우미에 위임하여 와일드카드를 캡처할 수 있습니다.
도우미가 형식 T에 이름을 붙이므로 리스트에 다시 쓸 수 있습니다.
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void reverse(List<?> list) { reverseHelper(list); }
private static <T> void reverseHelper(List<T> list) {
for (int i = 0, j = list.size() - 1; i < j; i++, j--) {
T tmp = list.get(i);
list.set(i, list.get(j));
list.set(j, tmp);
}
}
public static void main(String[] args) {
List<Integer> list = new ArrayList<>(List.of(1, 2, 3, 4));
reverse(list);
System.out.println(list);
}
}실제 API의 생산자 매개변수
호환되는 모든 원본에서 가져오는 addAll을 설계하려면 원본에 상한을 사용합니다.
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class Main {
static <T> void addAll(List<T> target, Collection<? extends T> source) {
for (T t : source) target.add(t);
}
public static void main(String[] args) {
List<Number> nums = new ArrayList<>();
addAll(nums, List.of(1, 2, 3));
System.out.println(nums);
}
}실제 API의 소비자 매개변수
결과를 쓰는 수신처 API는 대상에 하한을 사용하므로 호출자가 더 넓은 컨테이너를 제공할 수 있습니다.
import java.util.ArrayList;
import java.util.List;
public class Main {
static <T> void repeat(T value, int times, List<? super T> sink) {
for (int i = 0; i < times; i++) sink.add(value);
}
public static void main(String[] args) {
List<Object> out = new ArrayList<>();
repeat("hi", 3, out);
System.out.println(out);
}
}중첩 제네릭과 와일드카드
중첩 제네릭에는 와일드카드가 필요한 경우가 많습니다. 모든 종류의 리스트를 담은 리스트를 받는 메서드는 List<? extends List<?>>를 사용합니다.
import java.util.List;
public class Main {
static int countAll(List<? extends List<?>> lists) {
int total = 0;
for (List<?> inner : lists) total += inner.size();
return total;
}
public static void main(String[] args) {
System.out.println(countAll(List.of(List.of(1, 2), List.of("a", "b", "c"))));
}
}과도한 제네릭화 피하기
모든 메서드에 와일드카드가 필요한 것은 아닙니다. 호출자가 항상 하나의 형식만 전달한다면 일반 매개변수가 더 명확합니다.
불변성 때문에 실제 호출자가 막히는 상황을 확인했을 때 와일드카드를 추가하고, 미리 넣지는 마십시오.
import java.util.List;
public class Main {
// Simple and clear; no wildcard needed for this internal use
static String joinStrings(List<String> parts) {
return String.join(", ", parts);
}
public static void main(String[] args) {
System.out.println(joinStrings(List.of("a", "b", "c")));
}
}와일드카드 의도 문서화
와일드카드는 의도를 나타냅니다. ? extends T는 “이 값에서는 읽기만 한다”는 뜻입니다. ? super T는 “이 값에는 쓰기만 한다”는 뜻입니다.
올바른 와일드카드를 선택하면 계약을 강제하면서 코드 자체로 의도를 설명할 수 있습니다.
import java.util.ArrayList;
import java.util.List;
public class Main {
// src: read-only producer; dest: write-only consumer
static <T> void transfer(List<? extends T> src, List<? super T> dest) {
for (T t : src) dest.add(t);
}
public static void main(String[] args) {
List<Number> dest = new ArrayList<>();
transfer(List.of(1, 2), dest);
System.out.println(dest);
}
}제네릭 Stack API
Bloch가 제시한 유명한 스택 예제입니다. pushAll은 생산자에서 값을 소비하고(extends), popAll은 소비자에 값을 생산합니다(super).
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Deque;
import java.util.ArrayList;
import java.util.List;
public class Main {
static class Stack<E> {
private final Deque<E> items = new ArrayDeque<>();
void pushAll(Collection<? extends E> src) { for (E e : src) items.push(e); }
void popAll(Collection<? super E> dst) { while (!items.isEmpty()) dst.add(items.pop()); }
}
public static void main(String[] args) {
Stack<Integer> s = new Stack<>();
s.pushAll(List.of(1, 2, 3));
List<Number> out = new ArrayList<>();
s.popAll(out);
System.out.println(out);
}
}빠른 확인
API 설계 감각을 확인해 보십시오.
복습
와일드카드를 사용한 API 설계를 배웠습니다.
- 형식에 구애받지 않는 읽기 전용 메서드에는
List<?>를 사용합니다. - 유연성을 위해 매개변수에 PECS를 적용합니다.
- 와일드카드를 통해 써야 할 때는 비공개 도우미 캡처 기법을 사용합니다.
- 반환 형식에는 와일드카드를 사용하지 말고 과도하게 제네릭화하지 마십시오.
와일드카드와 PECS 과정을 완료했습니다.
public class Main {
public static void main(String[] args) {
System.out.println("Wildcards in APIs course complete");
}
}자주 묻는 질문
“API에서 와일드카드 사용” 강의는 무료인가요?
네 — “API에서 와일드카드 사용” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Java Academy 강의 전체를 잠금 해제할 수 있습니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“API에서 와일드카드 사용”에서 뭘 배우나요?
유연한 제네릭 메서드를 설계합니다 브라우저에서 직접 실행하는 실습 코드로 Java Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Java Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Java Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.
“API에서 와일드카드 사용” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Java Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Java Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.