[java] 수퍼 타입 ​​목록을 하위 유형 목록으로 캐스트하는 방법은 무엇입니까?

예를 들어, 두 개의 클래스가 있다고 가정 해 봅시다.

public class TestA {}
public class TestB extends TestA{}

나는 a를 반환하는 메소드를 가지고 List<TestA>있으며 그 목록의 모든 객체를 캐스트 TestB하여 a로 끝납니다 List<TestB>.



답변

List<TestB>거의 작업에 단순히 캐스팅 ; 그러나 한 유형의 매개 변수를 다른 유형으로 캐스트 할 수 없기 때문에 작동하지 않습니다. 그러나 중간 와일드 카드 유형을 통해 캐스트 할 수 있으며 허용되지 않습니다 (확인하지 않은 경고만으로 와일드 카드 유형과 캐스트 할 수 있으므로).

List<TestB> variable = (List<TestB>)(List<?>) collectionOfListA;


답변

제네릭 캐스팅은 불가능하지만 다른 방법으로 목록을 정의하면 TestB를 저장할 수 있습니다.

List<? extends TestA> myList = new ArrayList<TestA>();

목록의 객체를 사용할 때 유형 검사를 계속해야합니다.


답변

당신은 정말로 할 수 없습니다 * :

이 Java 자습서 에서 예를 가져옵니다.

두 가지 유형이 있다고 가정 A하고B 같은 그 B extends A. 그런 다음 다음 코드가 맞습니다.

    B b = new B();
    A a = b;

이전 코드는 B하위 클래스 이므로 유효합니다.A . 자, 함께 발생 List<A>하고 List<B>?

그것은 우리 List<B>의 하위 클래스가 아닙니다.List<A> 쓰기

    List<B> b = new ArrayList<>();
    List<A> a = b; // error, List<B> is not of type List<A>

또한, 우리 는 할 수 없습니다

    List<B> b = new ArrayList<>();
    List<A> a = (List<A>)b; // error, List<B> is not of type List<A>

* : 우리는 모두 공통의 부모를 필요로 캐스팅을 가능하게하는 방법 List<A>List<B>: List<?>예를 들면. 다음 유효합니다 :

    List<B> b = new ArrayList<>();
    List<?> t = (List<B>)b;
    List<A> a = (List<A>)t;

그러나 경고가 표시됩니다. @SuppressWarnings("unchecked")메소드 에 추가 하여 억제 할 수 있습니다 .


답변

Java 8을 사용하면 실제로

List<TestB> variable = collectionOfListA
    .stream()
    .map(e -> (TestB) e)
    .collect(Collectors.toList());


답변

그래도 잘못된 방향으로 캐스팅하고 있다고 생각합니다 … 메소드가 TestA객체 목록을 반환 하면 실제로 객체를 캐스팅하는 것이 안전하지 않습니다 TestB.

기본적으로 컴파일러에서 지원하지 않는 TestB유형에 대한 작업을 수행 할 수 있도록 컴파일러에 요청 TestA합니다.


답변

이것은 널리 참조되는 질문이며, 현재 답변은 왜 작동하지 않는 이유를 설명합니다 (또는 프로덕션 코드에서 결코 보지 못했던 해킹하고 위험한 솔루션을 제안합니다). 다른 답변을 추가하는 것이 적절하다고 생각합니다. 함정 및 가능한 해결책.


이것이 일반적으로 작동하지 않는 이유는 이미 다른 답변에서 지적되었습니다. 변환이 실제로 유효한지 여부는 원래 목록에 포함 된 객체의 유형에 따라 다릅니다. 목록에 유형이 아닌의 TestB다른 서브 클래스 인 유형의 오브젝트가 있으면 TestA캐스트가 유효 하지 않습니다.


물론 캐스트 유효 할 수 있습니다. 때로는 컴파일러에서 사용할 수없는 유형에 대한 정보가 있습니다. 이 경우 목록을 전송할 수 있지만 일반적으로 권장하지 않습니다 .

하나는 …

  • … 전체 목록을 캐스팅하거나
  • … 목록의 모든 요소를 ​​캐스트

첫 번째 접근 방식 (현재 허용되는 답변에 해당)의 의미는 미묘합니다. 언뜻 보면 제대로 작동하는 것 같습니다. 그러나 입력 목록에 잘못된 유형이있는 ClassCastException경우 코드에서 완전히 다른 위치에 a가 발생하고이를 디버그하고 잘못된 요소가 목록에서 미끄러지는 위치를 찾는 것이 어려울 수 있습니다. 최악의 문제는 목록을 캐스팅 한 후 누군가가 잘못된 요소를 추가하여 디버깅을 더욱 어렵게 한다는 것 입니다.

이러한 스퓨리어스를 디버깅하는 문제는 메소드 계열 ClassCastExceptions로 완화 할 수 있습니다 Collections#checkedCollection.


유형을 기준으로 목록 필터링

a에서 a List<Supertype> 로 변환하는 더 안전한 형식의 방법은 List<Subtype>실제로 목록을 필터링 하고 특정 유형의 요소 만 포함하는 새 목록을 만드는 것입니다. 그러한 방법의 구현에 대한 자유도가 어느 정도 있지만 (예를 들어 null, 엔트리 처리와 관련하여 ), 한 가지 가능한 구현은 다음과 같습니다.

/**
 * Filter the given list, and create a new list that only contains
 * the elements that are (subtypes) of the class c
 *
 * @param listA The input list
 * @param c The class to filter for
 * @return The filtered list
 */
private static <T> List<T> filter(List<?> listA, Class<T> c)
{
    List<T> listB = new ArrayList<T>();
    for (Object a : listA)
    {
        if (c.isInstance(a))
        {
            listB.add(c.cast(a));
        }
    }
    return listB;
}

이 방법은 다음 예제와 같이 임의의 목록을 필터링하기 위해 사용할 수 있습니다 (유형 매개 변수와 관련하여 주어진 하위 유형-슈퍼 유형 관계뿐 아니라).

// A list of type "List<Number>" that actually 
// contains Integer, Double and Float values
List<Number> mixedNumbers =
    new ArrayList<Number>(Arrays.asList(12, 3.4, 5.6f, 78));

// Filter the list, and create a list that contains
// only the Integer values:
List<Integer> integers = filter(mixedNumbers, Integer.class);

System.out.println(integers); // Prints [12, 78]


답변

당신은 캐스트 수 없습니다 List<TestB>List<TestA>스티브 쿠오 언급하지만 당신의 내용을 덤프 수 List<TestA>에를 List<TestB>. 다음을 시도하십시오 :

List<TestA> result = new List<TestA>();
List<TestB> data = new List<TestB>();
result.addAll(data);

나는이 코드를 시도하지 않았으므로 실수가있을 수 있지만 아이디어는 요소 (TestB 객체)를 List에 추가하는 데이터 객체를 반복해야한다는 것입니다. 나는 그것이 당신을 위해 작동하기를 바랍니다.