0Pricing
Java Academy · 강의

상한 와일드카드

생산자를 위한 ? extends T를 알아봅니다

상한 와일드카드은(는) CoddyKit의 무료 Java Academy 강의입니다. 이것은 4개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Java Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

제네릭은 불변입니다

Java에서 제네릭은 불변입니다. Integer가 Number이더라도 List<Integer>는 List<Number>가 아닙니다.

처음 접하는 사람에게는 놀라울 수 있지만, 이는 타입 시스템의 건전성을 유지합니다. 와일드카드는 이 제한을 안전하게 완화합니다.

import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Integer> ints = List.of(1, 2, 3);
        // List<Number> nums = ints; // would NOT compile
        System.out.println("List<Integer> is not List<Number>");
        System.out.println(ints);
    }
}

상한이 있는 와일드카드

? extends T는 상한이 있는 와일드카드입니다. 이는 "T 또는 T의 하위 타입인 어떤 알려지지 않은 타입"을 의미합니다.

List<? extends Number>는 List<Integer>, List<Double> 또는 List<Number>를 가리킬 수 있습니다.

import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<? extends Number> a = List.of(1, 2, 3);
        List<? extends Number> b = List.of(1.5, 2.5);
        System.out.println(a.get(0));
        System.out.println(b.get(0));
    }
}

생산자에서 읽기

? extends Number를 사용하면 요소를 Number로 안전하게 읽을 수 있습니다.

실제 요소 타입이 무엇이든 숫자 타입이거나 그 하위 타입임이 보장되므로 읽기가 타입 안전합니다.

import java.util.List;

public class Main {
    static double sum(List<? extends Number> list) {
        double total = 0;
        for (Number n : list) total += n.doubleValue();
        return total;
    }
    public static void main(String[] args) {
        System.out.println(sum(List.of(1, 2, 3)));
        System.out.println(sum(List.of(1.5, 2.5)));
    }
}

여기에는 쓸 수 없습니다

여기서 중요한 점은 ? extends Number 리스트에는 요소를 추가할 수 없다는 것입니다(null은 예외입니다).

컴파일러는 정확한 요소 형식을 알 수 없습니다. 실제로는 List<Integer>일 수 있으므로 Double을 추가하면 안전하지 않습니다.

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

public class Main {
    public static void main(String[] args) {
        List<? extends Number> list = new ArrayList<Integer>();
        // list.add(1);   // does NOT compile
        // list.add(1.5); // does NOT compile
        System.out.println("Cannot add to ? extends Number");
        System.out.println("size = " + list.size());
    }
}

쓰기가 금지되는 이유

쓰기가 허용된다고 가정해 보겠습니다. List<Integer>를 List<? extends Number>로 전달한 다음 Double을 추가할 수 있을 것입니다.

나중에 리스트를 List<Integer>로 읽는 코드에서 ClassCastException이 발생합니다. 컴파일러는 쓰기를 금지하여 이를 방지합니다.

import java.util.List;

public class Main {
    public static void main(String[] args) {
        // Conceptual: this is why add is blocked
        System.out.println("Writing a Double into a List<Integer> would corrupt it");
        System.out.println("so ? extends bans all adds");
    }
}

생산자 사용 사례

메서드가 값을 사용자에게 전달하기만 할 때(값을 읽어 올 때) ? extends T를 사용합니다.

원본 컬렉션에서 요소를 복사해 오는 메서드가 대표적인 예입니다.

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

public class Main {
    static List<Number> copyOut(List<? extends Number> source) {
        List<Number> dest = new ArrayList<>();
        for (Number n : source) dest.add(n);
        return dest;
    }
    public static void main(String[] args) {
        System.out.println(copyOut(List.of(1, 2, 3)));
    }
}

제한된 형식 매개변수와 와일드카드

<T extends Number>(제한된 형식 매개변수)와 ? extends Number(와일드카드)를 혼동하지 마십시오.

  • 형식 매개변수: 메서드 전체에서 사용할 형식에 이름을 붙입니다.
  • 와일드카드: 이름을 붙일 필요가 없을 때 사용하는, 이름이 없는 미지의 하위 형식입니다.
import java.util.List;

public class Main {
    static <T extends Number> T first(List<T> list) { return list.get(0); }
    public static void main(String[] args) {
        Integer i = first(List.of(10, 20));
        System.out.println(i);
    }
}

다중 제한

형식 매개변수에는 &를 사용해 여러 제한을 지정할 수 있습니다. 예를 들어 <T extends Number & Comparable<T>>와 같이 작성합니다.

와일드카드는 상한을 하나만 지원하므로 여러 제한이 필요하면 이름이 있는 형식 매개변수를 사용해야 합니다.

import java.util.List;

public class Main {
    static <T extends Number & Comparable<T>> T max(List<T> list) {
        T best = list.get(0);
        for (T t : list) if (t.compareTo(best) > 0) best = t;
        return best;
    }
    public static void main(String[] args) {
        System.out.println(max(List.of(3, 9, 1, 7)));
    }
}

Collections.max와 와일드카드

JDK는 상한을 폭넓게 사용합니다. 예를 들어 읽기 전용 컬렉션의 합을 계산하거나 내용을 살펴볼 때 활용합니다.

여기서는 생산자로 지정된 리스트에서 가장 큰 값을 찾습니다.

import java.util.List;

public class Main {
    static double largest(List<? extends Number> nums) {
        double max = Double.NEGATIVE_INFINITY;
        for (Number n : nums) max = Math.max(max, n.doubleValue());
        return max;
    }
    public static void main(String[] args) {
        System.out.println(largest(List.of(4, 2, 9, 1)));
        System.out.println(largest(List.of(0.5, 9.9, 3.3)));
    }
}

쓸 수 있는 값은 null뿐입니다

? extends T 컬렉션에 추가할 수 있는 유일한 값은 null입니다. null은 모든 참조 형식에 할당할 수 있기 때문입니다.

실제로 유용한 경우는 드물지만, add가 완전히 금지된 것이 아니라 사실상 사용할 수 없는 이유를 설명해 줍니다.

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

public class Main {
    public static void main(String[] args) {
        List<? extends Number> list = new ArrayList<Number>();
        // Only null is assignable; demonstrated conceptually
        System.out.println("Only null could be added; we avoid that");
        System.out.println("size = " + list.size());
    }
}

항상 Object로 읽어 올 수 있습니다

모든 형식은 Object를 상속하므로 어떤 와일드카드 리스트의 요소든 항상 Object로 읽을 수 있습니다.

상한이 Number라면 요소를 Number로 바로 읽을 수 있다는 이점도 있습니다.

import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<? extends Number> nums = List.of(1, 2.0, 3L);
        for (Object o : nums) System.out.println(o.getClass().getSimpleName());
    }
}

빠른 확인

상한에 대한 이해도를 확인해 보십시오.

복습

상한이 있는 와일드카드를 배웠습니다.

  • ? extends T는 T의 이름을 알 수 없는 하위 형식을 의미합니다.
  • 요소를 T로 읽을 수 있습니다(생산자).
  • null이 아닌 요소는 추가할 수 없습니다.
  • 제네릭은 불변이며, 와일드카드는 이를 안전하게 완화합니다.

다음에는 이와 반대되는 개념인 하한이 있는 와일드카드를 살펴봅니다.

import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<? extends Number> nums = List.of(1, 2, 3);
        System.out.println("Upper bound recap: read " + nums.get(0));
    }
}

자주 묻는 질문

“상한 와일드카드” 강의는 무료인가요?

네 — “상한 와일드카드” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Java Academy 강의 전체를 잠금 해제할 수 있습니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“상한 와일드카드”에서 뭘 배우나요?

생산자를 위한 ? extends T를 알아봅니다 브라우저에서 직접 실행하는 실습 코드로 Java Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Java Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Java Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 1번째 강의입니다.

“상한 와일드카드” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Java Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Java Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 상한 와일드카드
  2. 하한 와일드카드
  3. PECS 원칙
  4. API에서 와일드카드 사용
← Java Academy(으)로 돌아가기