1. 개요

이 빠른 사용방법(예제)에서는 <?><?유사점과 차이점을 확인할 수 있습니다 . Java Generics 에서 Object>확장합니다 .

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

2. 제네릭의 배경

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

예를 들어 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 로 캐스트하려고 할 때 두 번째 반복에서 런타임 오류가 발생합니다.

Generics는 우리에게 역할을합니다.

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 유형 Lista_string추가 할 수 없다고 알려줍니다 . 이는 런타임에 찾는 것보다 낫습니다.

컴파일러는 이미 알고 있기 때문에 또한, 명시 적 캐스팅이 필요하지 않습니다 IList의이 보유하고 정수 의. 또한 Unboxing의 마법으로 인해 Integer 유형 도 필요하지 않았 으며 원시 형식으로 충분합니다.

3. 제네릭의 와일드 카드

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

  • Unbounded Wildcards : 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정수 들뿐만 아니라 처리됩니다.

즉, 그것은 것을 의미  ? 그리고 ? extends Object 는이 예제에서 동의어입니다 .

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

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 연산자를 사용하면 ? Object> 확장 :

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. 결론

이 짧은 사용방법(예제)에서 <?><? 의 유사점과 차이점을 확인했습니다 . Object>를 확장합니다 .

대부분 유사하지만 수정 가능 여부에 따라 두 가지 사이에는 미묘한 차이가 있습니다.