programing

슈퍼타입 리스트를 서브타입 리스트에 캐스팅하려면 어떻게 해야 합니까?

prostudy 2022. 6. 7. 21:29
반응형

슈퍼타입 리스트를 서브타입 리스트에 캐스팅하려면 어떻게 해야 합니까?

예를 들어, 다음 두 가지 클래스가 있다고 가정합니다.

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

.List<TestA> 그 에 있는 오브젝트를 리리 all all에 하고 싶습니다.TestB는 결국...을 된다.List<TestB>.

캐스팅만 하면 됩니다.List<TestB>거의 동작하지만 어떤 파라미터의 범용 타입을 다른 파라미터에 캐스팅할 수 없기 때문에 동작하지 않습니다. 수 및 으로부터의 캐스트는

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

합니다.TestB in in :

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

목록의 개체를 사용할 때 여전히 유형 검사를 수행해야 합니다.

Java 8을 사용하면 실제로

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

정말로 할 수 없다*:

예시는 이 Java 튜토리얼에서 인용한 것입니다.

두 유형이 가정해 보겠습니다.A ★★★★★★★★★★★★★★★★★」BB extends A 다음

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

합니다.B는 의 입니다.A 그럼 ,, 럼, 럼, 럼은 어떻게 List<A> ★★★★★★★★★★★★★★★★★」List<B>무슨 일입니까?

보니, ★★★★★★★★★★★★★★★★★★.List<B> is is is is is isclassclassclassclassclass of의 가 아닙니다.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")당신의 방법에 따라.

가 "" 목록 "" " " " " " " " " " " "TestA합니다.TestB.

하여 '컴파일러'를 .TestBTestA그들을 지지하지 않습니다.

이것은 널리 언급되고 있는 질문으로, 현재의 답변은 왜 효과가 없는지를 주로 설명하고 있기 때문에(또는 생산 코드에서 결코 보고 싶지 않은 진부하고 위험한 솔루션을 제안하고), 다른 답변과 함정, 가능한 해결책을 추가하는 것이 적절하다고 생각합니다.


이것이 일반적으로 작동하지 않는 이유는 이미 다른 답변에서 지적되었습니다.변환이 실제로 유효한지는 원래 목록에 포함된 개체의 유형에 따라 달라집니다. TestB, ,른 、 른른 、 , , , , , , ,TestA캐스팅이 유효하지 않습니다.


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

한 명은...

  • ...모든 목록을 던지거나
  • ...목록의 모든 요소를 캐스팅합니다.

첫 번째 접근법(현재 승인된 답변에 해당)의 의미는 미묘하다.언뜻 보기에는 정상적으로 동작하는 것처럼 보일 수 있습니다. 입력 에 잘못된 경우 a가 됩니다.ClassCastException코드내의 전혀 다른 장소에 던져질 가능성이 있기 때문에, 이것을 디버깅 해, 리스트내의 잘못된 요소를 특정하는 것은 어려울 가능성이 있습니다.최악의 문제는 리스트가 캐스트된 후에 비활성 요소를 추가할 수도 있다는 것입니다.이 때문에 디버깅은 더욱 어려워집니다.

스플리어스 ClassCastExceptions메서드 패밀리로 경감할 수 있습니다.


유형에 따라 목록 필터링

List<Supertype> a까지List<Subtype>는 실제로 목록을 필터링하여 특정 유형의 요소만 포함하는 새 목록을 작성하는 것입니다.그러한 방법의 구현에는 어느 정도 자유도가 있다(예: 치료와 관련하여).null이치노1번, 1번, 1번, 1번, 1번

/**
 * 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>했듯이 Steve Kuo의 수 .List<TestA>List<TestB>을 시도해 다음을 시도해 보십시오.

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

이 코드를 사용해 본 적이 없기 때문에 오류가 있을 수 있지만, 데이터 오브젝트에서 요소(TestB 오브젝트)를 목록에 추가하는 것을 반복해야 합니다.그게 너한테 효과가 있길 바라.

은 '한 방법'을 실행하는 것입니다.AbstractList구현 중인 아이템을 캐스팅합니다.가 창작했습니다.ListUtil다음 중 하나:

public class ListUtil
{
    public static <TCastTo, TCastFrom extends TCastTo> List<TCastTo> convert(final List<TCastFrom> list)
    {
        return new AbstractList<TCastTo>() {
            @Override
            public TCastTo get(int i)
            {
                return list.get(i);
            }

            @Override
            public int size()
            {
                return list.size();
            }
        };
    }

    public static <TCastTo, TCastFrom> List<TCastTo> cast(final List<TCastFrom> list)
    {
        return new AbstractList<TCastTo>() {
            @Override
            public TCastTo get(int i)
            {
                return (TCastTo)list.get(i);
            }

            @Override
            public int size()
            {
                return list.size();
            }
        };
    }
}

하시면 됩니다.cast 내의 및 리스트 의 오브젝트를 캐스트하는 convert안전한 주조 방법.§:

void test(List<TestA> listA, List<TestB> listB)
{
    List<TestB> castedB = ListUtil.cast(listA); // all items are blindly casted
    List<TestB> convertedB = ListUtil.<TestB, TestA>convert(listA); // wrong cause TestA does not extend TestB
    List<TestA> convertedA = ListUtil.<TestA, TestB>convert(listB); // OK all items are safely casted
}

제가 아는 유일한 방법은 복사하는 것입니다.

List<TestB> list = new ArrayList<TestB> (
    Arrays.asList (
        testAList.toArray(new TestB[0])
    )
);

오브젝트 참조를 캐스팅할 때는 오브젝트 유형이 아니라 참조 유형만 캐스팅합니다.캐스팅을 해도 객체의 실제 유형은 변경되지 않습니다.

Java에는 개체 유형을 변환하는 암묵적인 규칙이 없습니다. (원본과 달리)

대신 어떤 유형을 다른 유형으로 변환하고 수동으로 호출하는 방법을 제공해야 합니다.

public class TestA {}
public class TestB extends TestA{ 
    TestB(TestA testA) {
        // build a TestB from a TestA
    }
}

List<TestA> result = .... 
List<TestB> data = new List<TestB>();
for(TestA testA : result) {
   data.add(new TestB(testA));
}

이것은 직접적인 지원이 있는 언어보다 더 장황하지만 효과가 있기 때문에 자주 이 작업을 수행할 필요는 없습니다.

TestA에수 .TestB .모든TestB는 입니다.TestA하지만 그 반대는 아니다.

다음 코드로 지정합니다.

TestA a = new TestA();
TestB b = (TestB) a;

은 두두 a a a a a a a a...를 던진다.ClassCastException.

할 수 있는 건 '''밖에 요''TestA오브젝트 자체가 다음과 같은 경우 참조TestB예를 들어 다음과 같습니다.

TestA a = new TestB();
TestB b = (TestB) a;

때문에 항상 는 않을 도 있습니다.TestATestB.

.selectInstances이클립스 컬렉션의 메서드.여기에는 새로운 컬렉션이 필요하지만 주물을 사용하는 일반적인 솔루션만큼 효율적이지는 않습니다.

List<CharSequence> parent =
        Arrays.asList("1","2","3", new StringBuffer("4"));
List<String> strings =
        Lists.adapt(parent).selectInstancesOf(String.class);
Assert.assertEquals(Arrays.asList("1","2","3"), strings);

포함했습니다.StringBuffer에서는, 「 」를 .selectInstances는 유형을 다운캐스트할 뿐만 아니라 컬렉션에 혼합 유형이 포함되어 있는지 여부도 필터링합니다.

주의: 저는 Eclipse Collections의 커밋입니다.

이는 유형 삭제로 인해 발생할 수 있습니다.곧 알게 될 것이다

List<TestA> x = new ArrayList<TestA>();
List<TestB> y = new ArrayList<TestB>();
x.getClass().equals(y.getClass()); // true

으로는 양쪽 타입입니다.List<Object>그렇기 때문에 캐스팅할 수 없습니다. 캐스팅할 것이 없습니다.

문제는 당신의 메서드에 TestB가 포함되어 있으면 TestA 목록이 반환되지 않는다는 것입니다.그렇다면 어떻게 해야 할까요?그러면 이 출연자:

class TestA{};
class TestB extends TestA{};
List<? extends TestA> listA;
List<TestB> listB = (List<TestB>) listA;

(Eclipse는 체크되지 않은 캐스팅에 대해 경고합니다.그것이 바로 당신이 하고 있는 일입니다)그럼 이걸로 문제를 해결할 수 있을까요?실제로 이 때문에 다음과 같이 할 수 있습니다.

List<TestA> badlist = null; // Actually contains TestBs, as specified
List<? extends TestA> talist = badlist;  // Umm, works
List<TextB> tblist = (List<TestB>)talist; // TADA!

정확히 네가 원했던 거 맞지?정확히 말하면:

List<TestB> tblist = (List<TestB>)(List<? extends TestA>) badlist;

컴파일이 잘 되는 것 같아요.

다음과 같은 기능을 구현하는 일부 도구 상자에서는 여전히 수동으로 목록을 제작할 수 없다는 것이 매우 이상합니다.

@SuppressWarnings({ "unchecked", "rawtypes" })
public static <T extends E, E> List<T> cast(List<E> list) {
    return (List) list;
}

물론 항목을 일일이 확인하는 것은 아니지만, 구현이 하위 유형만 제공한다는 것을 잘 알고 있다면 여기서 바로 피해야 할 것입니다.

class MyClass {
  String field;

  MyClass(String field) {
    this.field = field;
  }
}

@Test
public void testTypeCast() {
  List<Object> objectList = Arrays.asList(new MyClass("1"), new MyClass("2"));

  Class<MyClass> clazz = MyClass.class;
  List<MyClass> myClassList = objectList.stream()
      .map(clazz::cast)
      .collect(Collectors.toList());

  assertEquals(objectList.size(), myClassList.size());
  assertEquals(objectList, myClassList);
}

는 캐스팅하는 .List<Object>로로 합니다.List<MyClass>objectList에는 must must as as as as as as as as of of of of of of of of of of of of of of of of of of of of of 와 같은 유형의 합니다.MyClass그리고 이 예는 다음과 같이 생각할 수 있습니다.List<T>사용됩니다.이를 위해 get 필드Class<T> clazz 사용하다MyClass.class.

갱신했다

사실 강한 타입의 Java AFIK에서는 슈퍼 타입에서 서브 타입으로 캐스팅 할 수 없습니다.다만, 슈퍼 타입에 실장되어 있는 인터페이스를 도입할 수 있습니다.

interface ITest {
}

class TestA implements ITest {
}

class TestB extends TestA {
}

public class Main {

  public static void main(String[] args) {
    List<ITest> testAList = Arrays.asList(new TestA(), new TestA());

    Class<ITest> clazz = ITest.class;
    List<ITest> testBList = testAList.stream()
        .map(clazz::cast)
        .collect(Collectors.toList());

    System.out.println(testBList.size());
    System.out.println(testAList);
  }
}

또는 서브타입에서 슈퍼타입으로 캐스팅할 수 있습니다.다음과 같이 합니다.

class TestA {
}

class TestB extends TestA {
}

public class Main {

  public static void main(String[] args) {
    var a = new TestA();
    var b = new TestB();
    System.out.println(((TestA) b));

    List<TestB> testBList = Arrays.asList(new TestB(), new TestB());

    Class<TestA> clazz = TestA.class;
    List<TestA> testAList = testBList.stream()
        .map(clazz::cast)
        .collect(Collectors.toList());

    System.out.println(testAList.size());
    System.out.println(testAList);
  }
}

은 '아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아.generics사실, 저는 저의 휴지 상태 DAO 코드에서 가져왔습니다.

이거면 될 거야

List<TestA> testAList = new ArrayList<>();
List<TestB> testBList = new ArrayList<>()
testAList.addAll(new ArrayList<>(testBList));

언급URL : https://stackoverflow.com/questions/933447/how-do-you-cast-a-list-of-supertypes-to-a-list-of-subtypes

반응형