List <Map <문자열, 문자열 >> vs List <? Map <String, String >>을 확장 extends Map<String, String>> ? 차이가 없으면 사용하면 어떤 이점이

차이점이 있습니까?

List<Map<String, String>>

List<? extends Map<String, String>>

?

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



답변

차이점은 예를 들어

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의.


답변

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

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


답변

다른 답변에서 누락 된 것은 이것이 공통 및 상호 분산 및 하위 유형 및 수퍼 유형 (다형성) 및 특히 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>());

답변

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

나는 동의하는 것을 의미있어 방법이 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되므로 유형 시스템을 위험에 빠뜨리지 않고 하위 클래스와 함께 사용할 수 있습니다.


답변

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

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

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

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