List <SubClass>를 List <BaseClass>로 캐스팅하는 가장 효율적인 방법


134

나는 List<SubClass>으로 취급하고 싶습니다 List<BaseClass>. a SubClass를 a BaseClass로 캐스팅하는 것이 간단하기 때문에 문제가되지 않아야하는 것처럼 보이지만 컴파일러는 캐스트가 불가능하다고 불평합니다.

따라서 동일한 객체에 대한 참조를 얻는 가장 좋은 방법은 List<BaseClass>무엇입니까?

지금은 새 목록을 만들고 기존 목록을 복사하고 있습니다.

List<BaseClass> convertedList = new ArrayList<BaseClass>(listOfSubClass)

그러나 그것을 이해함에 따라 완전히 새로운 목록을 만들어야합니다. 가능하면 원본 목록을 참조하고 싶습니다!


3
당신이 여기에 대답 : stackoverflow.com/questions/662508/…
— lukastymo

답변:


175

이러한 종류의 할당 구문은 와일드 카드를 사용합니다.

List<SubClass> subs = ...;
List<? extends BaseClass> bases = subs;

그것은이 것을 깨닫게하는 것이 중요 List<SubClass>하다 하지 A를 교환 List<BaseClass>. 에 대한 참조를 유지하는 코드 List<SubClass>는 목록의 모든 항목이 SubClass입니다. 코드의 다른 부분이 같은리스트라고하면 List<BaseClass>, 컴파일러는 경우 불평 할 것이다 BaseClass또는 AnotherSubClass삽입된다. 그러나 이로 인해 ClassCastException첫 번째 코드 조각이 발생합니다 SubClass. 목록의 모든 항목이

일반 컬렉션은 Java의 배열과 동일하게 동작하지 않습니다. 배열은 공변량입니다. 즉, 다음을 수행 할 수 있습니다.

SubClass[] subs = ...;
BaseClass[] bases = subs;

배열은 요소의 유형을 "인식"하기 때문에 허용됩니다. 누군가가 참조 SubClass를 통해 배열 의 인스턴스가 아닌 것을 저장하려고 bases하면 런타임 예외가 발생합니다.

일반 컬렉션은 구성 요소 유형을 "알지" 않습니다 . 이 정보는 컴파일 타임에 "삭제"됩니다. 따라서 유효하지 않은 상점이 발생하면 런타임 예외를 발생시킬 수 없습니다. 대신 ClassCastException컬렉션에서 값을 읽을 때 코드에서 멀리 떨어져 있고 연결하기 어려운 지점에서 a가 발생합니다. 유형 안전에 대한 컴파일러 경고에주의를 기울이면 런타임시 이러한 유형 오류를 피할 수 있습니다.


SubClass 유형이 아닌 (또는 파생 된) 오브젝트를 검색 할 때 ClassCastException 대신 Arrays를 사용하면 삽입 할 때 ArrayStoreException이 발생합니다.
— Axel

두 목록을 동일하게 고려할 수없는 이유에 대한 설명에 특히 감사드립니다.
— 라일리 라크

Java 유형 시스템은 배열이 공변량 인 척하지만 실제로 ArrayStoreException에 의해 입증되지는 않습니다.
— Paŭlo Ebermann

38

erickson은 이미 왜 이것을 할 수 없는지 설명했지만 몇 가지 해결책이 있습니다.

기본 목록에서 요소 만 가져 오려면 원칙적으로 수신 방법은을 (를) 취하는 것으로 선언해야합니다 List<? extends BaseClass>.

그러나 그렇지 않고 변경할 수없는 경우 목록을로 감싸서 Collections.unmodifiableList(...)인수 매개 변수의 상위 유형 목록을 반환 할 수 있습니다. 삽입 시도시 UnsupportedOperationException을 발생시켜 형식 안전성 문제를 방지합니다.


큰! 그거 몰랐어요
— keuleJ

대단히 감사합니다!
— Woland

14

@erickson이 설명했듯이 원래 목록에 대한 참조를 원한다면 원래 선언에서 다시 사용하려는 코드가 있으면 해당 목록에 아무것도 삽입하지 마십시오. 그것을 얻는 가장 간단한 방법은 그것을 평범한 오래된 비 제네릭 목록으로 캐스팅하는 것입니다.

List<BaseClass> baseList = (List)new ArrayList<SubClass>();

List에 어떤 일이 발생하는지 모르고 List에 필요한 코드를 변경하여 List를 수락하도록 제안하는 경우에는 권장하지 않습니다.


3

아래는 유용한 스 니펫입니다. 새로운 배열 목록을 생성하지만 JVM 객체 생성은 중요하지 않습니다.

다른 답변이 불필요하게 복잡하다는 것을 알았습니다.

List<BaseClass> baselist = new ArrayList<>(sublist);

1
이 코드 스 니펫에 감사드립니다. 즉각적인 도움이 될 수 있습니다. 적절한 설명 은 왜 이것이 문제에 대한 좋은 해결책 인지 보여줌으로써 교육적 가치를 크게 향상시킬 것이며 , 유사하지만 동일하지 않은 질문을 가진 미래 독자들에게 더 유용 할 것입니다. 제발 편집 설명을 추가하고, 제한 및 가정이 적용 무엇의 표시를 제공하는 답변을. 특히, 이것이 새로운 목록을 만드는 질문의 코드와 어떻게 다릅니 까?
— Toby Speight

오버 헤드는 모든 참조를 (얕게) 복사해야하기 때문에 중요하지 않습니다. 따라서 목록의 크기에 따라 확장되므로 O (n) 연산입니다.
— john16384

2

이중 캐스트를 사용하여 원래 목록을 캐스트 한 답변을 놓쳤습니다. 따라서 여기에는 완전성이 있습니다.

List<BaseClass> baseList = (List<BaseClass>)(List<?>)subList;

아무것도 복사되지 않고 작업이 빠릅니다. 그러나 여기에서 컴파일러를 속이기 때문에 subList다른 하위 유형의 항목을 포함 하기 시작하는 방식으로 목록을 수정하지 않아야합니다 . 불변 목록을 다룰 때 이것은 일반적으로 문제가되지 않습니다.


1

List<BaseClass> convertedList = Collections.checkedList(listOfSubClass, BaseClass.class)


5
이 방법은 모든 인수와 결과가 동일한 유형 매개 변수를 사용하므로 작동하지 않아야합니다.
— Paŭlo Ebermann

이상하다, 네 말이 맞아 어떤 이유로 든, 나는이 방법이 일반 컬렉션을 업 캐스트하는 데 유용하다는 인상을 받았습니다. 이 방법으로 계속 사용할 수 있으며 억제 경고를 사용하려는 경우 "안전한"컬렉션을 제공합니다.
— jtahlborn

1

당신이하려는 것은 매우 유용하며 내가 작성한 코드에서 매우 자주해야한다는 것을 알았습니다. 사용 사례 예 :

인터페이스가 Foo있고 package-private 인스턴스를 만들고 관리 하는 zorking패키지 가 있다고 가정 해보십시오 . (매우 일반적인 시나리오입니다.) ZorkingFooManagerZorkingFoo implements Foo

따라서을 ZorkingFooManager포함해야 private Collection<ZorkingFoo> zorkingFoos하지만을 노출해야합니다 public Collection<Foo> getAllFoos().

대부분의 Java 프로그래머는 getAllFoos()new를 할당하고 ArrayList<Foo>모든 요소를 zorkingFoos채우고 반환하는 것으로 구현하기 전에 두 번 생각하지 않습니다 . 전 세계 수백만 대의 컴퓨터에서 실행되는 Java 코드가 소비하는 모든 클록 사이클의 약 30 %가 아무것도 생성하지 않은 채로 생성 된 후 가비지 수집 마이크로 초인 쓸모없는 ArrayLists 복사본을 생성하는 것 외에는 전혀 재미가 없습니다.

이 문제에 대한 해결책은 물론 컬렉션을 다운 캐스팅하는 것입니다. 가장 좋은 방법은 다음과 같습니다.

static <T,U extends T> List<T> downCastList( List<U> list )
{
    return castList( list );
}

어떤 castList()기능으로 우리를 가져옵니다 :

static <T,E> List<T> castList( List<E> list )
{
    @SuppressWarnings( "unchecked" )
    List<T> result = (List<T>)list;
    return result;
}

resultJava 변수의 왜곡으로 인해 중간 변수가 필요합니다.

  • return (List<T>)list;"체크되지 않은 캐스트"예외를 생성합니다. 여태까지는 그런대로 잘됐다; 하지만:

  • @SuppressWarnings( "unchecked" ) return (List<T>)list; 억제 경고 주석을 잘못 사용했습니다.

그래서, 사용에 정결하지 경우에도 @SuppressWarningsA의 return여분이 문제가 변수로 해결할 수있는 문제를 "결과"그래서 문, 그것은 할당에 사용하는 분명히 괜찮습니다. (어쨌든 컴파일러 또는 JIT에 의해 최적화되어야합니다.)


0

이와 같은 것도 작동해야합니다.

public static <T> List<T> convertListWithExtendableClasses(
    final List< ? extends T> originalList,
    final Class<T> clazz )
{
    final List<T> newList = new ArrayList<>();
    for ( final T item : originalList )
    {
        newList.add( item );
    }// for
    return newList;
}

왜 Eclipse에서 clazz가 필요한지 모르겠습니다.


0

모든 요소를 ​​캐스팅하는 것은 어떻습니까. 새 목록을 만들지 만 이전 목록의 원래 개체를 참조합니다.

List<BaseClass> convertedList = listOfSubClass.map(x -> (BaseClass)x).collect(Collectors.toList());

이것은 하위 클래스 목록을 수퍼 클래스로 캐스트하는 완전한 코드입니다.
— kanaparthikiran

0

이것은 서브 클래스 목록을 수퍼 클래스로 캐스팅하기 위해 Generics를 사용하는 완전한 작업 코드입니다.

서브 클래스 유형을 전달하는 호출자 메소드

List<SubClass> subClassParam = new ArrayList<>();    
getElementDefinitionStatuses(subClassParam);

기본 클래스의 하위 유형을 허용하는 수신자 메소드

private static List<String> getElementDefinitionStatuses(List<? extends 
    BaseClass> baseClassVariableName) {
     return allElementStatuses;
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.