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


29

이 예에서 :

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>이 경우 왜 첫 번째 컴파일을하지 않는 이유는 무엇입니까?


의견은 긴 토론을위한 것이 아닙니다. 이 대화는 채팅 으로 이동 되었습니다 .
Samuel Liew

[1] Java 제네릭 형식 : List <? extends Number>와 List <T extends Number> 는 같은 질문을하는 것처럼 보이지만 관심이있을 수 있지만 실제로는 중복되지 않습니다. [2] 이것은 좋은 질문이지만 제목에는 최종 문장에서 요구되는 특정 질문이 제대로 반영되지 않습니다.
skomisa


여기서 설명 어떻습니까?
jrook

답변:


14

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

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>>());
    }
}

[1] 나는 당신이 ? extends Number오히려 의미한다고 생각합니다 ? extends Numeric. [2] "List <? extends Number> 및 List <Integer>의 경우가 아님"에 대한 귀하의 주장 이 잘못되었습니다. @VinceEmigh가 이미 지적했듯이 메소드를 작성하여 static void demo(List<? extends Number> lst) { }this demo(new ArrayList<Integer>());또는 this 처럼 호출 demo(new ArrayList<Float>());하면 코드가 컴파일되고 실행됩니다. 아니면 내가 말한 것을 잘못 읽거나 오해하고 있습니까?
skomisa

@ 두 경우 모두 맞습니다. 당신의 두 번째 요점에 관해서, 나는 그것을 오도하는 방식으로 썼습니다. 나는 List<? extends Number>그 자체가 아니라 전체지도의 유형 매개 변수를 의미 했습니다. 의견 감사합니다.
Andronicus

같은 이유로 @skomisa는 List<Number>포함하지 않습니다 List<Integer>. 함수가 있다고 가정합니다 static void check(List<Number> numbers) {}. 호출 할 때 check(new ArrayList<Integer>());컴파일되지 않으면 메소드를로 정의해야합니다 static void check(List<? extends Number> numbers) {}. 지도는 동일하지만 더 많은 중첩이 있습니다.
Andronicus

1
@skomisa Number는 list의 유형 매개 변수와 동일 ? extends하며 공변량으로 만들 려면 추가해야하며 공분산 List<? extends Number>의 유형 매개 변수 Map이기도합니다 ? extends.
Andronicus

1
확인. 다중 레벨 와일드 카드 (일명 "중첩 와일드 카드" ?) 에 대한 솔루션을 제공하고 관련 JLS 참조에 링크되었으므로 현상금이 있습니다.
skomisa

6

전화에서 :

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>>논쟁으로 받아 들일 수 없습니다 .


그렇다면 유효한 부름은 무엇입니까 doesntCompile? 그냥 궁금 해서요
Xtreme Biker

1
@XtremeBiker doesntCompile(new HashMap<Integer, List<? extends Number>>());는 작동 doesntCompile(new HashMap<>());합니다.
skomisa

@ XtremeBiker, 이것조차도 작동합니다. Map <Integer, List <? 확장 번호 >> map = new HashMap <정수, 목록 <? Number >> ()를 확장합니다. map.put (null, new ArrayList <정수> ()); doestCompile (map);
MOnkey

"할당 HashMap<Integer, List<Integer>>할 수 없는 " "왜 할당 할 수 없는지 자세히 설명해 주시겠습니까?
Dev Null

@DevNull 위의 내 업데이트 참조
Maurice Perry

2

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

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은 이미 공변량입니다. 공분산을 허용하려면 둘러싸는 유형을 공분산으로 만들어야합니다.


어떻게 즉 <Pair<Integer>>작동하지 않습니다 <Pair<? extends Number>>하지만 함께 작동합니까 <T extends Number> <Pair<T>>?
jaco0646

@ jaco0646 기본적으로 OP와 동일한 질문을하고 있으며 Andronicus의 답변 이 승인되었습니다. 해당 답변의 코드 예제를 참조하십시오.
skomisa

@skomisa, 그렇습니다. 몇 가지 이유로 같은 질문을합니다. 하나는이 답변이 실제로 OP의 질문을 다루지 않는 것 같습니다. 그러나 두 가지는이 답변이 이해하기 쉽다는 것입니다. 리드 나 비 - 중첩 된 제네릭 대 중첩, 또는 이해하기 것을 나는 어떤 방법으로 안드로에서 답을 따라 갈 수 없어 T?. 문제의 일부는 Andronicus가 설명의 중요한 포인트에 도달 할 때 사소한 예만 사용하는 다른 스레드를 지연 시킨다는 것입니다. 나는 여기에 더 명확하고 완전한 대답을 얻기를 희망했습니다.
jaco0646

1
알았어. Angelika Langer의 "Java Generics FAQs-Type Arguments" 문서 에는 다중 레벨 (즉, 중첩) 와일드 카드 란 무엇입니까? 라는 제목의 FAQ가 있습니다. . 이것이 OP의 질문에서 제기 된 문제를 설명하는 데 가장 좋은 출처입니다. 중첩 된 와일드 카드 규칙은 간단하거나 직관적이지 않습니다.
skomisa

1

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

솔직히 방법을 말하기 #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 가 게시 한 답변 은 절대적으로 정확합니다. 나는 그것이 확실하지 않다는 것을 확신하지 못하므로 더 광범위한 게시물을 추가하려고 시도했다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.