2015-01-08 5 views
1

가 실패 :Java Generics는 변하지 않습니다? 아래의 코드를 컴파일

public static void swap(List<?> list, int i, int j) { 
     list.set(i, list.set(j, list.get(i))); 
} 

같은 :

Swap.java:5: set(int,capture#282 of ?) in List<capture#282 of ?> cannot be applied to (int,Object) 
      list.set(i, list.set(j, list.get(i))); 

그러나 경우에 나는이 작업을 수행 : 완벽하게 작동

public static void swap(List<?> list, int i, int j) { 
     swapHelper(list, i, j); 
} 

private static <E> void swapHelper(List<E> list, int i, int j) { list.set(i, list.set(j, list.get(i))); 
} 

그것은.

는하지만 여기에 기본 의심의 여지가있다. 제네릭은 불변이라고 알려져 있으므로 List<String>List<Object>의 하위 유형이 아닙니다. 맞습니까?

그런 경우 위 방법을 사용하면 List<?>List<E>에 전달할 수 있습니까? 어떻게 작동합니까?

+0

와일드 카드는 표준 할당 할 수있는 검사와 다르게 작동합니다. – hexafraction

답변

7

대답은 와일드 카드입니다. 자바는 모두 객체의 모음입니다 가정 동안

List<?>List<Object> 과 동일하지 않습니다, 후자는 List<Object> 외에 어떤 List<T>의 유형과 일치하지 반면, 다른 List<T>의 유형과 일치합니다 전.

예 :이 매개 변수의로이 기능은 List<Object>을 받아

public void doSomething(List<Object> list); 

. 그러나 :

public void doSomething(List<?> list); 

은 매개 변수로 모든 List<T>을 허용합니다. generic constraints와 함께 사용하면

은 매우 유용 할 수 있습니다. 예를 들어 당신은 당신이 할 수 번호를 조작하는 함수를 작성 (등 Integer의, Float들) 원하는 경우 : 당신이 wildcard 캡처를 사용하고 있기 때문에

public void doSomethingToNumbers(List<? extends Number> numbers) { ... } 
+0

''public void doSomethingToNumbers (List numbers)'는'와 동일합니다. public void doSomething (List numbers)' – Dima

+0

또한 어느 쪽이 맞나요? 매우 유용합니다. 그러나 나는이 예에서 와일드 카드 구문이 더 명확하고 프로그래머의 의도를 더 잘 전달한다고 믿는다. – Assaf

1

.

경우에 따라 컴파일러에서 와일드 카드의 형식을 유추합니다. 예를 들어, 목록은 목록으로 정의 될 수 있지만, 식을 평가할 때, 컴파일러는 코드에서 특정 유형을 유추합니다. 이 시나리오를 와일드 카드 캡처라고합니다.

컴파일러는 도우미 메서드 덕분에 추론을 사용하여 호출에서 캡처 변수 T를 결정합니다.

0

List<?>는 "알 수없는 유형의 목록"을 의미한다. List<String>을 전달할 수 있습니다. 하위 클래스 (아니지만)가 아닌 "알 수없는 유형"이 던진 것을 수락해야하기 때문입니다.

유형을 알 수 없으므로 요소 유형 (예 : set 또는 add 등)을 알아야하는 조작을 수행 할 수 없습니다.

List<E>는 일반적인이다.그것은 다소 유사하더라도 비록 List<?>과 같지 않습니다. 하나의 차이점은, 언급 한 바와 같이 set 또는 add과 같은 작업을 수행 할 수 있습니다. 요소의 유형을 알 수 있기 때문입니다.

함수 매개 변수로 Whildcards는 매우 유용하지 않습니다 (<E> void foo(List<E> l)void foo(List<?> l)과 크게 다르지 않습니다). 그들은 또한 모든 컴파일 타임 타입 체크를 우회하여 generics의 목적을 무효화하고, 실제로 피해야합니다.

a를 반환 형식에 와일드 카드 사용보다 일반적인 (그리고 덜 해로운) : List<? extends DataIterface> getData()getDataDataInterface를 구현하는 일부 개체의 목록을 반환한다는 것을 의미하지만, 그들의 구체적인 유형을 말하지 않을 것이다. 이는 종종 인터페이스에서 구현 세부 사항을 분리하기 위해 API 디자인에서 수행됩니다. 일반적으로 API로 반환 된 객체 목록을 다른 API 메소드에 전달할 수 있으며 객체를 받아 처리합니다. 이 개념을 existential types이라고합니다.

또한 위 예제의 getData은 발신자가 List<E>을 반환한다고 선언 한 것과 달리 일부 조건 (발신자의 도메인 외부)에 따라 다른 유형의 목록을 반환 할 수 있습니다.이 경우 발신자는 어떤 식 으로든 예상 된 형식을 지정합니다.

관련 문제