[java] <의 차이점은 무엇입니까? 베이스 확장> 및 <T 확장베이스>?

이 예에서 :

import java.util.*;

public class Example {
    static void doesntCompile(Map<Integer, List<? extends Number>> map) {}
    static <T extends Number> void compiles(Map<Integer, List<T>> map) {}

    static void function(List<? extends Number> outer)
    {
        doesntCompile(new HashMap<Integer, List<Integer>>());
        compiles(new HashMap<Integer, List<Integer>>());
    }
}

doesntCompile() 컴파일하지 못했습니다 :

Example.java:9: error: incompatible types: HashMap<Integer,List<Integer>> cannot be converted to Map<Integer,List<? extends Number>>
        doesntCompile(new HashMap<Integer, List<Integer>>());
                      ^

compiles()컴파일러가 승인하는 동안 .

이 답변 의 유일한 차이점은 그와 달리이라고 설명하고 <? ...>, <T ...>경우 될 것 같지 않는, 나중에 유형을 참조 할 수 있습니다.

차이점은 무엇이며 <? extends Number>그리고 <T extends Number>이 경우 왜 첫 번째 컴파일을하지 않는 이유는 무엇입니까?



답변

다음과 같은 서명으로 메소드를 정의합니다.

static <T extends Number> void compiles(Map<Integer, List<T>> map) {}

다음과 같이 호출하십시오.

compiles(new HashMap<Integer, List<Integer>>());

jls §8.1.2에서 우리는 다음과 같은 사실을 발견했습니다.

일반 클래스 선언은 유형 인수로 유형 매개 변수 섹션을 호출 할 때마다 하나씩 매개 변수화 된 유형 세트 (§4.5)를 정의합니다 . 이러한 매개 변수화 된 유형은 모두 런타임에 동일한 클래스를 공유합니다.

즉, 유형 T이 입력 유형과 일치하고 할당됩니다 Integer. 서명이 효과적으로됩니다 static void compiles(Map<Integer, List<Integer>> map).

그것이 올 때 doesntCompile방법, JLS는 (하위 유형의 규칙을 정의 §4.5.1 내 옆에 굵은 글씨를)

유형 인수 T1은 T2로 표시되는 유형 세트가 다음 규칙의 재귀 및 전이 폐쇄에서 T1으로 표시되는 유형 세트의 서브 세트 인 경우 T2 <= T1로 작성된 다른 유형 인수 T2를 포함한다고합니다 ( 여기서 <:은 하위 유형을 나타냅니다 (§4.10).

  • ? T <= 연장 T <: S 인 경우 S를 확장

  • ? T <= 연장

  • ? 슈퍼 T <=? S <: T 인 경우 슈퍼 S

  • ? 슈퍼 T <=?

  • ? 슈퍼 T <=? 객체를 확장

  • T <= T

  • T <=? T를 연장

  • T <=? 슈퍼 T

즉, ? extends Number실제로 포함 Integer하거나 List<? extends Number>포함 List<Integer>하고 있지만 Map<Integer, List<? extends Number>>및 의 경우는 아닙니다 Map<Integer, List<Integer>>. 이 주제에 대한 자세한 내용은 이 SO 스레드에서 찾을 수 있습니다 . 다음과 ?같은 하위 유형이 있다고 선언 하여 와일드 카드를 사용하여 버전을 작동 시킬 수 있습니다 List<? extends Number>.

public class Example {
    // now it compiles
    static void doesntCompile(Map<Integer, ? extends List<? extends Number>> map) {}
    static <T extends Number> void compiles(Map<Integer, List<T>> map) {}

    public static void main(String[] args) {
        doesntCompile(new HashMap<Integer, List<Integer>>());
        compiles(new HashMap<Integer, List<Integer>>());
    }
}


답변

전화에서 :

compiles(new HashMap<Integer, List<Integer>>());

T는 Integer와 일치하므로 인수의 유형은 Map<Integer,List<Integer>>입니다. 메서드의 경우에는 해당되지 않습니다. doesntCompile인수의 유형은 Map<Integer, List<? extends Number>>호출의 실제 인수에 관계없이 유지됩니다 . 그리고에서 할당 할 수 없습니다 HashMap<Integer, List<Integer>>.

최신 정보

doesntCompile방법 에서는 다음과 같은 일을 방해하는 것이 없습니다.

static void doesntCompile(Map<Integer, List<? extends Number>> map) {
    map.put(1, new ArrayList<Double>());
}

분명히, 그것은 HashMap<Integer, List<Integer>>논쟁으로 받아 들일 수 없습니다 .


답변

간단한 데모 예. 같은 예를 아래와 같이 시각화 할 수 있습니다.

static void demo(List<Pair<? extends Number>> lst) {} // doesn't work
static void demo(List<? extends Pair<? extends Number>> lst) {} // works
demo(new ArrayList<Pair<Integer>()); // works
demo(new ArrayList<SubPair<Integer>()); // works for subtype too

public static class Pair<T> {}
public static class SubPair<T> extends Pair<T> {}

List<Pair<? extends Number>>다중 레벨 와일드 카드 유형 인 반면 List<? extends Number>표준 와일드 카드 유형입니다.

와일드 카드 유형의 유효한 구체적인 인스턴스화 List<? extends Number>에는 포함 Number및 모든 하위 유형이 포함 Number되는 반면 List<Pair<? extends Number>>인수 인 경우 인수 유형의 유형 인수이고 일반 유형의 구체적인 인스턴스화가 있습니다.

제네릭은 변하지 않으므로 Pair<? extends Number>와일드 카드 유형은에만 사용할 수 있습니다 Pair<? extends Number>>. 내부 유형 ? extends Number은 이미 공변량입니다. 공분산을 허용하려면 둘러싸는 유형을 공분산으로 만들어야합니다.


답변

일반적인 와일드 카드 문서, 특히 와일드 카드 사용 지침 을 살펴 보는 것이 좋습니다.

솔직히 방법을 말하기 #doesntCompile

static void doesntCompile(Map<Integer, List<? extends Number>> map) {}

그리고 같은 전화

doesntCompile(new HashMap<Integer, List<Integer>>());

근본적으로 부정확하다

합법적 인 구현을 추가합시다 :

    static void doesntCompile(Map<Integer, List<? extends Number>> map) {
        List<Double> list = new ArrayList<>();
        list.add(0.);
        map.put(0, list);
    }

풋 그래서 더블 번호, 확장 때문에, 정말 괜찮 List<Double>뿐만 아니라 절대적으로 괜찮 List<Integer>오른쪽?

그러나 귀하의 모범에서 여기로 전달 하는 것이 여전히 합법적 이라고 가정 new HashMap<Integer, List<Integer>>()하십니까?

컴파일러는 그렇게 생각하지 않으며 그러한 상황을 피하기 위해 최선을 다하고 있습니다.

메소드 #compile을 사용하여 동일한 구현을 시도하면 컴파일러는 이중 목록을 맵에 넣을 수 없습니다.

    static <T extends Number> void compiles(Map<Integer, List<T>> map) {
        List<Double> list = new ArrayList<>();
        list.add(10.);
        map.put(10, list); // does not compile
    }

기본적으로 아무것도 넣을 수는 없지만 or 또는 or 또는으로 List<T>해당 메소드를 호출하는 것이 안전합니다 .new HashMap<Integer, List<Integer>>()new HashMap<Integer, List<Double>>()new HashMap<Integer, List<Long>>()new HashMap<Integer, List<Number>>()

간단히 말해, 컴파일러로 속이려고 노력하고 있으며 그러한 속임수에 대해 공정하게 방어합니다.

주의 : Maurice Perry 가 게시 한 답변 은 절대적으로 정확합니다. 나는 그것이 확실하지 않다는 것을 확신하지 못하므로 더 광범위한 게시물을 추가하려고 시도했다.


답변