List <Map <문자열, 문자열 >> vs List <? Map <String, String >>을 확장


129

차이점이 있습니까?

List<Map<String, String>>

List<? extends Map<String, String>>

?

차이가 없으면 사용하면 어떤 이점이 ? extends있습니까?


2
나는 자바를 사랑하지만 이것은 좋지 않은 것들 중 하나입니다 ...
Mite Mitreski

4
우리가 그것을 "확장하는 것"이라고 읽으면 분명해 진다고 생각합니다.
쓰레기

약 3 일 만에 12K 회 이상의 놀라운 조회수? !!
Eng.Fouad

5
해커 뉴스 첫 페이지에 도달했습니다. 축하합니다.
r3st0r3

1
@Eng. 여기에 있습니다. 더 이상 첫 페이지에는 없지만 어제있었습니다. news.ycombinator.net/item?id=3751901 (어제는 인도에서 일요일 중순 일요일입니다)
r3st0r3

답변:


180

차이점은 예를 들어

List<HashMap<String,String>>

이다

List<? extends Map<String,String>>

그러나

List<Map<String,String>>

그래서:

void withWilds( List<? extends Map<String,String>> foo ){}
void noWilds( List<Map<String,String>> foo ){}

void main( String[] args ){
    List<HashMap<String,String>> myMap;

    withWilds( myMap ); // Works
    noWilds( myMap ); // Compiler error
}

당신은 생각 ListHashMap의가 있어야한다 ListMap가없는 이유들,하지만 좋은 이유가있다 :

당신이 할 수 있다고 가정하십시오 :

List<HashMap<String,String>> hashMaps = new ArrayList<HashMap<String,String>>();

List<Map<String,String>> maps = hashMaps; // Won't compile,
                                          // but imagine that it could

Map<String,String> aMap = Collections.singletonMap("foo","bar"); // Not a HashMap

maps.add( aMap ); // Perfectly legal (adding a Map to a List of Maps)

// But maps and hashMaps are the same object, so this should be the same as

hashMaps.add( aMap ); // Should be illegal (aMap is not a HashMap)

왜 그래서는 ListHashMap의는 안 ListMap의.


6
그러나 HashMapA는 Map다형성 때문입니다.
Eng.Fouad

46
네,하지만 ListHashMaps는하지 ListMap들.
진실성


좋은 예입니다. 또한 주목할만한 가치는 선언 할 경우에도 있다는 것입니다 List<Map<String,String>> maps = hashMaps; 그리고 HashMap<String,String> aMap = new HashMap<String, String>();당신은 여전히 찾을 것입니다 maps.add(aMap);불법 동안은 hashMaps.add(aMap);합법적이다. 목적은 잘못된 유형의 추가를 방지하는 것이지만 올바른 유형의 추가를 허용하지 않습니다 (컴파일러가 컴파일 시간 동안 "올바른"유형을 결정할 수 없음)
Raze

@Raze 아니오, 실제로 s 목록에 a 를 추가 할 있습니다 . 올바르게 읽으면 두 예제 모두 합법적입니다. HashMapMap
진실성

24

List<NavigableMap<String,String>>첫 번째 유형과 같은 유형의 표현식을 지정할 수 없습니다 .

(당신이 할당 할 수없는 이유를 알고 싶은 경우 List<String>List<Object>참조 엄청나게 SO에 대한 다른 질문.)


1
더 설명 할 수 있습니까? 실습에 대한 이해가되지 않거나 링크가 없습니까?
Samir Mangroliya

3
@Samir 무엇을 설명 하시겠습니까? List<String>의 하위 유형이 List<Object>아닙니까? -예를 들어 stackoverflow.com/questions/3246137/… 참조
Tom Hawtin-tackline

2
이것은의 유무에 따른 차이점을 설명하지 않습니다 ? extends. 또한 수퍼 / 서브 타입 또는 공 / 반대 분산 (있는 경우)과의 상관 관계도 설명하지 않습니다.
Abel

16

다른 답변에서 누락 된 것은 이것이 공통 및 상호 분산 및 하위 유형 및 수퍼 유형 (다형성) 및 특히 Java와 어떻게 관련이 있는지에 대한 참조입니다. 이것은 OP에 의해 잘 이해 될 수 있지만, 경우에 따라 다음과 같습니다.

공분산

당신은 클래스가있는 경우 Automobile, 다음 CarTruck자신의 서브 타입이다. 모든 자동차는 자동차 유형의 변수에 할당 될 수 있으며, 이는 OO에서 잘 알려져 있으며 다형성이라고합니다. 공분산은 제네릭 또는 대리자가있는 시나리오에서 동일한 원칙을 사용하는 것을 말합니다. Java에는 아직 위임자가 없으므로이 용어는 제네릭에만 적용됩니다.

공분산은 생각하지 않고 작동 할 것으로 예상되는 표준 다형성으로 생각하는 경향이 있습니다.

List<Car> cars;
List<Automobile> automobiles = cars;
// You'd expect this to work because Car is-a Automobile, but
// throws inconvertible types compile error.

그러나 오류의 원인은 맞습니다. 상속 List<Car> 하지 않으므로List<Automobile> 서로 할당 할 수 없습니다. 제네릭 형식 매개 변수 만 상속 관계를 갖습니다. Java 컴파일러는 단순히 시나리오를 제대로 이해하기에 충분하지 않다고 생각할 수도 있습니다. 그러나 힌트를 제공하여 컴파일러를 도울 수 있습니다.

List<Car> cars;
List<? extends Automobile> automobiles = cars;   // no error

공분산

공분산의 역수는 공분산입니다. 공분산에서 모수 유형은 하위 유형 관계를 가져야하고, 반공산에서는 수퍼 유형 관계를 가져야합니다. 이것은 상속 상한으로 간주 될 수 있습니다 : 지정된 유형을 포함하여 모든 수퍼 유형이 허용됩니다.

class AutoColorComparer implements Comparator<Automobile>
    public int compare(Automobile a, Automobile b) {
        // Return comparison of colors
    }

이것은 Collections.sort 와 함께 사용할 수 있습니다 :

public static <T> void sort(List<T> list, Comparator<? super T> c)

// Which you can call like this, without errors:
List<Car> cars = getListFromSomewhere();
Collections.sort(cars, new AutoColorComparer());

객체를 비교하고 모든 유형과 함께 사용하는 비교기로 호출 할 수도 있습니다.

대비 또는 공분산을 언제 사용해야합니까?

약간 구약은 아마도 묻지 않았지만 질문에 대한 답을 이해하는 데 도움이됩니다. 일반적으로 무언가 를 얻을 때 공분산을 사용하고 무언가 를 넣을 때에는 반공 분산을 사용하십시오. 이것은 스택 오버플로 질문대한 답변 에서 가장 잘 설명 됩니다. Java 제네릭에서 어떻게 공차가 사용됩니까? .

그래서 무엇입니까 List<? extends Map<String, String>>

를 사용 extends하므로 공분산 규칙이 적용됩니다. 여기에 맵 목록이 있으며 목록에 저장 한 각 항목은 맵에서 가져 Map<string, string>오거나 파생 되어야 합니다. 성명은 List<Map<String, String>>에서 파생 할 수 Map있지만,해야 . Map

따라서 다음을 TreeMap상속 하기 때문에 다음이 작동합니다 Map.

List<Map<String, String>> mapList = new ArrayList<Map<String, String>>();
mapList.add(new TreeMap<String, String>());

그러나 이것은하지 않습니다 :

List<? extends Map<String, String>> mapList = new ArrayList<? extends Map<String, String>>();
mapList.add(new TreeMap<String, String>());

공분산 제약 조건을 만족하지 않기 때문에 이것은 작동하지 않습니다.

List<? extends Map<String, String>> mapList = new ArrayList<? extends Map<String, String>>();
mapList.add(new ArrayList<String>());   // This is NOT allowed, List does not implement Map

또 뭐요?

이것은 아마도 명백하지만 extends키워드 를 사용하는 것은 나머지가 아닌 해당 매개 변수에만 적용 된다는 것을 이미 언급했을 것 입니다. 즉, 다음은 컴파일되지 않습니다.

List<? extends Map<String, String>> mapList = new List<? extends Map<String, String>>();
mapList.add(new TreeMap<String, Element>())  // This is NOT allowed

키가 문자열 인 맵에서 모든 유형을 허용한다고 가정하면 extend각 유형 매개 변수에 사용할 수 있습니다 . 즉, XML을 처리하고 AttrNode, Element 등을 맵에 저장하려는 경우 다음과 같은 작업을 수행 할 수 있습니다.

List<? extends Map<String, ? extends Node>> listOfMapsOfNodes = new...;

// Now you can do:
listOfMapsOfNodes.add(new TreeMap<Sting, Element>());
listOfMapsOfNodes.add(new TreeMap<Sting, CDATASection>());

"그래서 무엇을해야합니까 ..."에 아무것도 컴파일되지 않습니다.
NobleUplift

@NobleUplift : 오류 메시지가 표시되지 않으면 도움을주기가 어렵습니다. 대안으로, SO에 대한 새로운 질문을하여 답변을 얻고 더 큰 성공 기회를 얻는 것도 고려하십시오. 위의 코드는 코드 조각 일 뿐이며 시나리오에서 구현 한 내용에 따라 다릅니다.
Abel

새로운 질문이 없습니다. 개선되었습니다. List<? extends Map<String, String>> mapList = new ArrayList<? extends Map<String, String>>(); mapList.add(new TreeMap<String, String>());결과 found: ? extends java.util.Map<java.lang.String,java.lang.String> required: class or interface without bounds. List<Map<String, String>> mapList = new ArrayList<Map<String, String>>(); mapList.add(new TreeMap<String, String>());완벽하게 작동합니다. 마지막 예는 분명히 맞습니다.
NobleUplift 2009 년

@NobleUplift : 죄송합니다. 오랜 시간 여행하면 돌아올 때 해결되며 눈부신 오류를 지적 해 주셔서 감사합니다! :)
Abel

문제 없습니다. 문제가 해결되어 다행입니다. 승인하려는 경우 수정했습니다.
NobleUplift

4

오늘은이 기능을 사용 했으므로 여기에 매우 신선한 실제 사례가 있습니다. (클래스 및 메소드 이름을 일반 이름으로 변경하여 실제 요점을 방해하지 않도록했습니다.)

나는 동의하는 것을 의미있어 방법이 SetA내가 원래이 서명에 쓴 개체를 :

void myMethod(Set<A> set)

그러나 실제로 Set의 하위 클래스 s로 호출하려고합니다 A. 그러나 이것은 허용되지 않습니다! 그 이유 는 호출자의 사이트에 오브젝트가 선언 된 서브 타입이 아닌 유형의 myMethod오브젝트를 추가 할 수 있기 때문에 가능하면 유형 시스템을 중단시킬 수 있기 때문입니다.setAset

이제이 메소드 서명을 대신 사용하면 의도 한대로 작동하기 때문에 구조의 제네릭이 제공됩니다.

<T extends A> void myMethod(Set<T> set)

메소드 본문에 실제 유형을 사용할 필요가없는 경우 더 짧거나 짧습니다.

void myMethod(Set<? extends A> set)

이런 식으로 set의 유형은의 실제 하위 유형의 객체 모음이 A되므로 유형 시스템을 위험에 빠뜨리지 않고 하위 클래스와 함께 사용할 수 있습니다.


0

언급했듯이 List를 정의하는 두 가지 버전이 있습니다.

  1. List<? extends Map<String, String>>
  2. List<?>

2는 매우 열려 있습니다. 모든 객체 유형을 보유 할 수 있습니다. 주어진 유형의 맵을 원할 경우 유용하지 않을 수 있습니다. 경우에는 누군가가 실수로, 예를 들어,지도의 다른 유형을 넣습니다 Map<String, int>. 소비자의 방법이 깨질 수 있습니다.

List주어진 유형의 객체를 보유 할 수 있도록하기 위해 Java generics가 도입되었습니다 ? extends. 따라서 # 1에서는 유형 List에서 파생 된 모든 객체를 보유 할 수 있습니다 Map<String, String>. 다른 유형의 데이터를 추가하면 예외가 발생합니다.

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