Menu
Coddy logo textTech

와일드카드 (?, extends, super)

Coddy Java 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 87개 중 57번째.

bounded 타입 매개변수는 제네릭을 특정 타입으로 제한하지만, 기존 제네릭 타입을 다루면서 정확한 타입 매개변수를 모르거나 신경 쓰지 않는 경우에는 어떻게 해야 할까요? 이럴 때 wildcards가 사용됩니다.

제한되지 않은 와일드카드 ?는 알 수 없는 타입을 나타냅니다. 매개변수화된 모든 타입을 허용하려 할 때 유용합니다:

public static void printList(List<?> list) {
    for (Object item : list) {
        System.out.println(item);
    }
}

printList(new ArrayList<String>());   // 작동함
printList(new ArrayList<Integer>());  // 작동함

상한 와일드카드 ? extends Type는 지정된 타입 또는 해당 타입의 모든 하위 클래스를 허용합니다. 제네릭 구조에서 읽기가 필요할 때 사용하세요:

public static double sumNumbers(List<? extends Number> numbers) {
    double sum = 0;
    for (Number n : numbers) {
        sum += n.doubleValue();
    }
    return sum;
}

sumNumbers(Arrays.asList(1, 2, 3));        // Integer 리스트
sumNumbers(Arrays.asList(1.5, 2.5));       // Double 리스트

하한 와일드카드 ? super Type은 지정된 타입 또는 그 슈퍼클래스 중 하나를 허용합니다. 제네릭 구조에 쓰기를 해야 할 때 사용하세요:

public static void addIntegers(List<? super Integer> list) {
    list.add(1);
    list.add(2);
}

List<Number> numbers = new ArrayList<>();
addIntegers(numbers);  // 작동함 - Number는 Integer의 상위 타입입니다

유용한 지침: 읽기만 할 때(생산자)는 extends를 사용하고, 쓰기만 할 때(소비자)는 super를 사용하세요. 이를 PECS 원칙이라고 합니다. Producer Extends, Consumer Super의 약자입니다.

challenge icon

챌린지

쉬움

세 가지 와일드카드 유형을 모두 보여 주는 동물원 먹이 공급 시스템을 만들어 봅시다! 동물과 음식의 계층 구조를 만들고, 제한되지 않은 와일드카드, 상한 와일드카드, 하한 와일드카드를 사용하여 다양한 먹이 공급 상황을 처리하는 유틸리티 메서드를 작성합니다.

코드를 네 개의 파일로 구성합니다.

  • Food.java: 음식을 위한 간단한 클래스 계층 구조를 만듭니다. private 필드 name (String), 이를 초기화하는 constructor, 그리고 getName() 메서드를 가진 기본 클래스 Food로 시작합니다. 그런 다음 같은 파일에 두 개의 하위 클래스를 만듭니다. Meat는 Food를 extends하며 (constructor에서 super(name)을 call), Vegetable도 Food를 extends합니다 (동일한 패턴).
  • Animal.java: private 필드 species (String), constructor, 그리고 getSpecies() 메서드를 가진 기본 클래스 Animal을 만듭니다. 두 개의 하위 클래스 CarnivoreHerbivore를 만들고, 각각 Animal을 extends하며 부모 constructor를 call합니다.
  • ZooFeeder.java: 여기서 와일드카드가 진가를 발휘합니다! 세 개의 static 메서드를 가진 유틸리티 클래스를 만듭니다.

    printInventory(List<?> items) - 제한되지 않은 와일드카드를 사용하여 모든 리스트를 출력합니다. 리스트를 순회하며 각 item을 toString()을 사용해 한 줄에 하나씩 출력합니다.

    calculateTotalFood(List<? extends Food> foods) - 상한 와일드카드를 사용하여 어떤 Food 유형의 리스트에서도 읽습니다. item의 개수를 int로 return합니다. 이는 producer에서 읽는 것을 보여 줍니다.

    addMeatToStock(List<? super Meat> stock, String meatName) - 하한 와일드카드를 사용하여 Meat을 담을 수 있는 리스트에 씁니다. 주어진 이름으로 새로운 Meat object를 create하고 stock에 add합니다. 이는 consumer에 쓰는 것을 보여 줍니다.

  • Main.java: 동물원 먹이 공급 시스템을 하나로 결합합니다! 두 개의 입력을 받습니다. 고기 이름 (String)과 채소 이름 (String)입니다.

    먼저 ArrayList<Food>를 create하고, 고기 이름을 사용한 Meat object와 채소 이름을 사용한 Vegetable object를 add합니다. Food inventory:를 출력한 다음 이 리스트를 사용하여 printInventory를 call합니다.

    다음으로 빈 줄을 출력한 후, 음식 리스트에서 calculateTotalFood를 사용하여 Total food items: [count]를 출력합니다.

    마지막으로 meatStock이라고 called되는 새로운 ArrayList<Food>를 create합니다. 빈 줄을 출력한 후 Adding to meat stock...을 출력합니다. 이 리스트와 문자열 "Beef"를 사용하여 addMeatToStock를 call합니다. 그런 다음 Meat stock after adding:을 출력하고 meat stock에서 printInventory를 call합니다.

입력은 순서대로 두 개를 받습니다. 고기 이름과 채소 이름입니다.

Food class에서 toString()을 Override하여 음식의 이름을 return하도록 하세요. 그래야 출력이 올바르게 작동합니다. 각 와일드카드 유형이 서로 다른 목적을 수행한다는 점에 주목하세요. Object로 읽기만 하면 될 때 최대한의 유연성을 제공하는 ?, 특정 유형을 읽을 때 사용하는 ? extends, 컬렉션에 쓸 때 사용하는 ? super입니다!

직접 해보기

import java.util.Scanner;
import java.util.ArrayList;
import java.util.List;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String meatName = scanner.nextLine();
        String vegetableName = scanner.nextLine();
        
        // TODO: ArrayList<Food>를 생성하고 Meat와 Vegetable 객체를 추가하세요
        
        // TODO: Print "Food inventory:" then call printInventory
        
        // TODO: Print blank line, then "Total food items: [count]" using calculateTotalFood
        
        // TODO: meatStock이라는 새로운 ArrayList<Food>를 생성하세요
        
        // TODO: Print blank line, then "Adding to meat stock..."
        
        // TODO: meatStock과 "Beef"로 addMeatToStock을 호출하세요
        
        // TODO: Print "Meat stock after adding:" then call printInventory on meatStock
    }
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: 온라인 Java 컴파일러