차이점이 있습니까?
List<Map<String, String>>
과
List<? extends Map<String, String>>
?
차이가 없으면 사용하면 어떤 이점이 ? extends
있습니까?
차이점이 있습니까?
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
}
당신은 생각 List
의 HashMap
의가 있어야한다 List
의 Map
가없는 이유들,하지만 좋은 이유가있다 :
당신이 할 수 있다고 가정하십시오 :
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)
왜 그래서는 List
의 HashMap
의는 안 List
의 Map
의.
HashMap
A는 Map
다형성 때문입니다.
List
의 HashMap
s는하지 List
의 Map
들.
List<Map<String,String>> maps = hashMaps;
그리고 HashMap<String,String> aMap = new HashMap<String, String>();
당신은 여전히 찾을 것입니다 maps.add(aMap);
불법 동안은 hashMaps.add(aMap);
합법적이다. 목적은 잘못된 유형의 추가를 방지하는 것이지만 올바른 유형의 추가를 허용하지 않습니다 (컴파일러가 컴파일 시간 동안 "올바른"유형을 결정할 수 없음)
HashMap
Map
List<NavigableMap<String,String>>
첫 번째 유형과 같은 유형의 표현식을 지정할 수 없습니다 .
(당신이 할당 할 수없는 이유를 알고 싶은 경우 List<String>
에 List<Object>
참조 엄청나게 SO에 대한 다른 질문.)
List<String>
의 하위 유형이 List<Object>
아닙니까? -예를 들어 stackoverflow.com/questions/3246137/… 참조
? extends
. 또한 수퍼 / 서브 타입 또는 공 / 반대 분산 (있는 경우)과의 상관 관계도 설명하지 않습니다.
다른 답변에서 누락 된 것은 이것이 공통 및 상호 분산 및 하위 유형 및 수퍼 유형 (다형성) 및 특히 Java와 어떻게 관련이 있는지에 대한 참조입니다. 이것은 OP에 의해 잘 이해 될 수 있지만, 경우에 따라 다음과 같습니다.
당신은 클래스가있는 경우 Automobile
, 다음 Car
과 Truck
자신의 서브 타입이다. 모든 자동차는 자동차 유형의 변수에 할당 될 수 있으며, 이는 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>());
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>());
완벽하게 작동합니다. 마지막 예는 분명히 맞습니다.
오늘은이 기능을 사용 했으므로 여기에 매우 신선한 실제 사례가 있습니다. (클래스 및 메소드 이름을 일반 이름으로 변경하여 실제 요점을 방해하지 않도록했습니다.)
나는 동의하는 것을 의미있어 방법이 Set
의 A
내가 원래이 서명에 쓴 개체를 :
void myMethod(Set<A> set)
그러나 실제로 Set
의 하위 클래스 s로 호출하려고합니다 A
. 그러나 이것은 허용되지 않습니다! 그 이유 는 호출자의 사이트에 오브젝트가 선언 된 서브 타입이 아닌 유형의 myMethod
오브젝트를 추가 할 수 있기 때문에 가능하면 유형 시스템을 중단시킬 수 있기 때문입니다.set
A
set
이제이 메소드 서명을 대신 사용하면 의도 한대로 작동하기 때문에 구조의 제네릭이 제공됩니다.
<T extends A> void myMethod(Set<T> set)
메소드 본문에 실제 유형을 사용할 필요가없는 경우 더 짧거나 짧습니다.
void myMethod(Set<? extends A> set)
이런 식으로 set
의 유형은의 실제 하위 유형의 객체 모음이 A
되므로 유형 시스템을 위험에 빠뜨리지 않고 하위 클래스와 함께 사용할 수 있습니다.
언급했듯이 List를 정의하는 두 가지 버전이 있습니다.
List<? extends Map<String, String>>
List<?>
2는 매우 열려 있습니다. 모든 객체 유형을 보유 할 수 있습니다. 주어진 유형의 맵을 원할 경우 유용하지 않을 수 있습니다. 경우에는 누군가가 실수로, 예를 들어,지도의 다른 유형을 넣습니다 Map<String, int>
. 소비자의 방법이 깨질 수 있습니다.
List
주어진 유형의 객체를 보유 할 수 있도록하기 위해 Java generics가 도입되었습니다 ? extends
. 따라서 # 1에서는 유형 List
에서 파생 된 모든 객체를 보유 할 수 있습니다 Map<String, String>
. 다른 유형의 데이터를 추가하면 예외가 발생합니다.