1. 개요

이 빠른 사용방법(예제)에서는 <?><? 의 유사점과 차이점을 살펴보겠습니다 . extends Object> in Java Generics .

그러나 이것은 고급 주제이므로 문제의 핵심으로 뛰어들기 전에 주제에 대한 기본적인 이해를 얻는 것이 필수적입니다.

2. 제네릭의 배경

제네릭은 런타임 오류를 제거하고 형식 안전성을 강화하기 위해 JDK 5에 도입되었습니다. 이 추가적인 유형 안전성은 일부 사용 사례에서 캐스팅을 제거하고 프로그래머가 일반 알고리즘을 작성할 수 있도록 하여 두 가지 모두 더 읽기 쉬운 코드로 이어질 수 있습니다.

예를 들어 JDK 5 이전 버전에서는 캐스팅을 사용하여 List의 요소로 작업해야 했습니다. 이로 인해 특정 런타임 오류 클래스가 생성되었습니다.

List aList = new ArrayList();
aList.add(new Integer(1));
aList.add("a_string");
        
for (int i = 0; i < aList.size(); i++) {
    Integer x = (Integer) aList.get(i);
}

이제 이 코드에는 우리가 해결하고자 하는 두 가지 문제가 있습니다.

  • aList 에서 값을 추출하려면 명시적 캐스트가 필요 합니다 . 유형은 왼쪽의 변수 유형에 따라 다릅니다. 이 경우에는 정수 입니다.
  • a_stringInteger 로 캐스트하려고 할 때 두 번째 반복에서 런타임 오류가 발생합니다.

제네릭은 다음과 같은 역할을 수행합니다.

List<Integer> iList = new ArrayList<>();
iList.add(1);
iList.add("a_string"); // compile time error

for (int i = 0; i < iList.size(); i++) {
    int x = iList.get(i);
}

컴파일러는 Integer 유형 의 List 에 a_string추가하는 것이 불가능하다고 알려줄 것입니다 . 이는 런타임에 알아내는 것보다 낫습니다.

게다가 컴파일러는 iListInteger 를 가지고 있다는 것을 이미 알고 있기 때문에 명시적인 캐스팅이 필요하지 않습니다 . 또한 언박싱의 마법 때문에 Integer 유형 도 필요하지 않았고 기본 형식으로도 충분합니다.

3. 제네릭의 와일드카드

물음표 또는 와일드카드는 제네릭에서 알 수 없는 유형을 나타내는 데 사용됩니다. 세 가지 형식이 있을 수 있습니다.

  • 무제한 와일드카드 : List<?> 는 알 수 없는 유형의 List을 나타냅니다.
  • 상한 와일드카드 : List<? extends Number> 는 Number 또는 IntegerDouble 과 같은 하위 유형 의 List을 나타냅니다.
  • 하한 와일드카드 : List<? super Integer> 는 Integer 또는 그 상위 유형인 NumberObject 의 List을 나타냅니다.

이제 Object 는 Java의 모든 유형의 고유한 상위 유형이므로 알 수 없는 유형을 나타낼 수도 있다고 생각하고 싶을 것입니다. 즉, List<?>List<Object> 는 동일한 용도로 사용할 수 있습니다. 하지만 그렇지 않습니다.

다음 두 가지 방법을 고려해 보겠습니다.

public static void printListObject(List<Object> list) {    
    for (Object element : list) {        
        System.out.print(element + " ");    
    }        
}    

public static void printListWildCard(List<?> list) {    
    for (Object element: list) {        
        System.out.print(element + " ");    
    }     
}

Integer List이 주어지면 다음 과 같이 말하십시오.

List<Integer> li = Arrays.asList(1, 2, 3);

printListObject(li) 는 컴파일되지 않고 다음 오류가 발생합니다.

The method printListObject(List<Object>) is not applicable for the arguments (List<Integer>)

반면에 printListWildCard(li) 는 컴파일 되어 콘솔에 1 2 3 을 출력합니다.

4. <?><? extends Object> – 유사점

위의 예에서 printListWildCard 의 메서드 서명을 다음 과 같이 변경하면

public static void printListWildCard(List<? extends Object> list)

printListWildCard(List<?> list) 와 같은 방식으로 작동합니다 . 이는 Object 가 모든 Java 객체의 상위 유형이고 기본적으로 모든 것이 Object 를 확장 한다는 사실 때문입니다 . 따라서 List of Integer 도 처리됩니다.

요컨대 ?그리고 ? extends 객체 는 이 예제에서 동의어입니다 .

대부분의 경우 사실 이지만 몇 가지 차이점도 있습니다 . 다음 섹션에서 살펴보겠습니다.

5. <?><? extends Object> – 차이점

수정 가능한 유형은 컴파일 시 유형이 지워지지 않는 유형입니다. 다시 말해, 수정 불가능한 유형의 런타임 표현은 컴파일 타임 대응 항목보다 정보가 적습니다. 그 중 일부가 지워지기 때문입니다.

일반적으로 매개변수화된 유형은 수정할 수 없습니다. 즉 , List<String>Map<Integer, String> 은 재정의할 수 없습니다. 컴파일러는 해당 유형을 지우고 각각 ListMap 으로 처리합니다 .

이 규칙의 유일한 예외는 무제한 와일드카드 유형입니다. 이는 List<?>Map<?,?> 이 수정 가능함을 의미 합니다.

반면에 List<? extends Object> 는 수정 가능하지 않습니다 . 미묘하지만 이것은 눈에 띄는 차이입니다.

수정 불가능 유형은 instanceof 연산자 또는 배열 요소 와 같은 특정 상황 에서 사용할 수 없습니다 .

따라서 다음과 같이 작성하면

List someList = new ArrayList<>();
boolean instanceTest = someList instanceof List<?>

이 코드는 컴파일되고 instanceTesttrue 입니다.

하지만 List<? 에서 instanceof 연산자를 사용한다면 ? 객체 확장> :

List anotherList = new ArrayList<>();
boolean instanceTest = anotherList instanceof List<? extends Object>;

그러면 2행이 컴파일되지 않습니다.

마찬가지로 아래 스니펫에서 1행은 컴파일되지만 2행은 컴파일되지 않습니다.

List<?>[] arrayOfList = new List<?>[1];
List<? extends Object>[] arrayOfAnotherList = new List<? extends Object>[1]

6. 결론

이 짧은 사용방법(예제)에서 <?><? 의 유사점과 차이점을 살펴보았습니다 . 확장 개체> .

대부분 유사하지만, 재화 가능 여부에 따라 둘 사이에는 미묘한 차이가 있습니다.

Generic footer banner