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

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

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

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

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

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



답변

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

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가 발생합니다. 유형 안전에 대한 컴파일러 경고에주의를 기울이면 런타임시 이러한 유형 오류를 피할 수 있습니다.


답변

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

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

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


답변

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

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

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


답변

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

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

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


답변

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

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

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


답변

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


답변

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

인터페이스가 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에 의해 최적화되어야합니다.)