제네릭, 어레이 및 ClassCastException
-
21-08-2019 - |
문제
나는 여기서 모르는 미묘한 일이 있어야한다고 생각합니다. 다음을 고려하세요:
public class Foo<T> {
private T[] a = (T[]) new Object[5];
public Foo() {
// Add some elements to a
}
public T[] getA() {
return a;
}
}
주요 방법이 다음을 포함한다고 가정합니다.
Foo<Double> f = new Foo<Double>();
Double[] d = f.getA();
당신은 얻을 것입니다 CastClassException
메시지와 함께 java.lang.Object
캐스트 할 수 없습니다 java.lang.Double
.
누구든지 이유를 말해 줄 수 있습니까? 내 이해 ClassCastException
캐스트 할 수없는 유형에 물체를 캐스팅하려고 할 때 던져진 것입니다. 즉, 서브 클래스는 인스턴스가 아닌 (문서를 인용하기 위해). 예 :
Object o = new Double(3.);
Double d = (Double) o; // Working cast
String s = (String) o; // ClassCastException
그리고 내가 이것을 할 수있는 것 같습니다. 만약에 a
그냥 a T
배열 대신 T[]
, 우리는 얻을 수 있습니다 a
문제없이 캐스팅하십시오. 왜 배열이 이것을 깨뜨리는가?
감사.
해결책
Foo<Double> f = new Foo<Double>();
이 버전의 일반 클래스 foo를 사용하면 멤버 변수에 대해 a
, 컴파일러는 본질적 으로이 라인을 취하고 있습니다.
private T[] a = (T[]) new Object[5];
그리고 교체 T
~와 함께 Double
이것을 얻으려면 :
private Double[] a = (Double[]) new Object[5];
물체에서 두 배로 캐스트 할 수 없으므로 ClassScastException.
업데이트 및 설명 : 실제로, 일부 테스트 코드를 실행 한 후 ClasscastException은 이것보다 더 미묘합니다. 예를 들어,이 주요 방법은 예외없이 잘 작동합니다.
public static void main(String[] args) {
Foo<Double> f = new Foo<Double>();
System.out.println(f.getA());
}
할당을 시도 할 때 문제가 발생합니다 f.getA()
유형의 참조 Double[]
:
public static void main(String[] args) {
Foo<Double> f = new Foo<Double>();
Double[] a2 = f.getA(); // throws ClassCastException
System.out.println(a2);
}
이는 멤버 변수에 대한 유형 정보가 있기 때문입니다 a
런타임에 지워집니다. 제네릭은 유형 안전성 만 제공합니다 컴파일 타임 (나는 어떻게 든 초기 게시물에서 이것을 무시하고있었습니다). 따라서 문제는 아닙니다
private T[] a = (T[]) new Object[5];
런타임 에이 코드는 실제로 이루어지기 때문입니다
private Object[] a = new Object[5];
문제는 방법 결과가 발생할 때 발생합니다 getA()
, 런타임에 실제로 반환합니다 Object[]
, 유형의 참조에 할당됩니다 Double[]
-이 명령문은 객체를 두 배로 캐스트 할 수 없기 때문에 ClassCastException을 던집니다.
업데이트 2: 마지막 질문에 대답하기 위해 "왜 배열이 이것을 깨뜨리는가?" 답은 언어 사양이 일반 어레이 생성을 지원하지 않기 때문입니다. 자세한 내용은이 포럼 게시물을 참조하십시오 - 거꾸로 호환 되려면 런타임의 T 유형에 대해서는 알려진 바가 없습니다.
다른 팁
@mattb의 설명에는 약간의 오류가있을 수 있습니다.
오류는입니다 ~ 아니다
java.lang.object는 java.lang.double에 캐스트 할 수 없습니다.
그것은이다:
ljava.lang.object; [ljava.lang.double에 캐스트 할 수 없습니다
l은 배열을 의미합니다. 즉, 오류는 an입니다 정렬 객체의 객체는 이중 배열로 캐스트 할 수 없습니다. 이것은 다음과 같은 경우입니다.
Object[] oarr = new Object[10];
Double[] darr = (Double[]) oarr;
이것은 분명히 허용되지 않습니다.
TypeSafe 배열을 만드는 문제의 경우, 또 다른 대안은 init 및 array.newinstance를 사용하는 클래스 객체를 제외한 것입니다.
import java.lang.reflect.Array;
class Foo<T> {
private T[] a ;
public Foo(Class<T> tclass) {
a = (T[]) Array.newInstance(tclass, 5);
}
public T[] getA() {
return a;
}
public static <T> Foo<T> create(Class<T> tclass) {
return new Foo<T>(tclass);
}
}
class Array1
{
public static final void main(final String[] args) {
Foo<Double> f = Foo.create(Double.class);
Double[] d = f.getA();
}
}
@Matt B : 답변 해주셔서 감사합니다! 매우 도움이됩니다.
관심있는 사람들을위한 해결 방법을 찾았습니다. Geta 방법에 초기화 된 배열을 채우십시오. 이렇게하면 유형 정보를 사용할 수 있습니다.
public class Foo<T> {
private T[] a = (T[]) new Object[5];
public Foo() {
// Add some elements to a
}
public T[] getA(T[] holdA) {
// Check whether holdA is null and handle it...then:
holdA = (T[]) Array.newInstance(holdA.getClass().getComponentType(), a.length);
System.arraycopy(a, 0, holdA, 0, a.length);
return holdA;
}
}
그런 다음 주요 방법을 위해 :
public static void main(String[] args) {
Foo<Double> f = new Foo<Double>();
Double[] a2 = new Double[1];
a2 = f.getA(a2);
}