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.