일반 배열을 만드는 방법은 무엇입니까? [복제]
이 질문에 이미 답변이 있습니다.
제네릭과 배열 간의 연결을 이해하지 못합니다.
일반 유형으로 배열 참조를 만들 수 있습니다.
private E[] elements; //GOOD
그러나 일반 유형으로 배열 객체를 만들 수 없습니다.
elements = new E[10]; //ERROR
그러나 작동합니다.
elements = (E[]) new Object[10]; //GOOD
배열과 제네릭을 혼동해서는 안됩니다. 그들은 잘 어울리지 않습니다. 배열과 제네릭 유형이 유형 검사를 시행하는 방법에는 차이가 있습니다. 우리는 배열이 수정되었다고 말하지만 제네릭은 그렇지 않습니다. 결과적으로 배열 및 제네릭에서 작동하는 이러한 차이점을 볼 수 있습니다.
배열은 공변이고 제네릭은 다음이 아닙니다.
그게 무슨 뜻입니까? 이제 다음 할당이 유효하다는 것을 알고 있어야합니다.
Object[] arr = new String[10];
기본적으로,는 Object[]
의 슈퍼 유형입니다 String[]
때문에, Object
의 슈퍼 유형입니다 String
. 제네릭에서는 그렇지 않습니다. 따라서 다음 선언은 유효하지 않으며 컴파일되지 않습니다.
List<Object> list = new ArrayList<String>(); // Will not compile.
이유는 제네릭이 변하지 않기 때문입니다.
유형 검사 시행 :
제네릭은 컴파일 시간에 더 강력한 유형 검사를 시행하기 위해 Java에 도입되었습니다. 따라서 제네릭 유형은 erasure 유형 으로 인해 런타임에 유형 정보가 없습니다 . 따라서 a List<String>
는 정적 유형 List<String>
이지만 동적 유형은 List
.
그러나 배열에는 구성 요소 유형의 런타임 유형 정보가 포함됩니다. 런타임에 배열은 배열 저장소 검사를 사용하여 실제 배열 유형과 호환되는 요소를 삽입하고 있는지 확인합니다. 따라서 다음 코드 :
Object[] arr = new String[10];
arr[0] = new Integer(10);
잘 컴파일되지만 ArrayStoreCheck의 결과로 런타임에 실패합니다. 제네릭을 사용하면 컴파일러가 위와 같이 이와 같은 참조 생성을 피하여 컴파일 시간 검사를 제공하여 런타임 예외를 방지하려고 시도하므로 불가능합니다.
그렇다면 Generic Array Creation의 문제는 무엇입니까?
구성 요소 유형이 유형 매개 변수 , 구체적인 매개 변수 유형 또는 경계 와일드 카드 매개 변수 유형 인 배열 생성 은 유형이 안전하지 않습니다 .
아래 코드를 고려하십시오.
public <T> T[] getArray(int size) {
T[] arr = new T[size]; // Suppose this was allowed for the time being.
return arr;
}
의 유형은 T
런타임에 알 수 없으므로 생성 된 배열은 실제로 Object[]
. 따라서 런타임에 위의 방법은 다음과 같습니다.
public Object[] getArray(int size) {
Object[] arr = new Object[size];
return arr;
}
이제이 메서드를 다음과 같이 호출한다고 가정합니다.
Integer[] arr = getArray(10);
여기에 문제가 있습니다. Object[]
의 참조 에을 할당했습니다 Integer[]
. 위의 코드는 잘 컴파일되지만 런타임에 실패합니다.
이것이 일반적인 어레이 생성이 금지 된 이유입니다.
왜 타입 캐스팅 new Object[10]
이 E[]
작동합니까?
이제 마지막 의심, 왜 아래 코드가 작동하는지 :
E[] elements = (E[]) new Object[10];
위의 코드는 위에서 설명한 것과 동일한 의미를 갖습니다. 알 수없는 구성 요소 유형의 배열로 형변환 할 때 컴파일러에서 Unchecked Cast 경고를 표시 합니다. 즉, 런타임에 캐스트가 실패 할 수 있습니다. 예를 들어 위의 방법에 해당 코드가있는 경우 :
public <T> T[] getArray(int size) {
T[] arr = (T[])new Object[size];
return arr;
}
다음과 같이 호출합니다.
String[] arr = getArray(10);
이것은 ClassCastException으로 런타임에 실패합니다. 따라서이 방법은 항상 작동하지 않습니다.
유형의 배열을 만드는 것은 List<String>[]
어떻습니까?
The issue is the same. Due to type erasure, a List<String>[]
is nothing but a List[]
. So, had the creation of such arrays allowed, let's see what could happen:
List<String>[] strlistarr = new List<String>[10]; // Won't compile. but just consider it
Object[] objarr = strlistarr; // this will be fine
objarr[0] = new ArrayList<Integer>(); // This should fail but succeeds.
Now the ArrayStoreCheck in the above case will succeed at runtime although that should have thrown an ArrayStoreException. That's because both List<String>[]
and List<Integer>[]
are compiled to List[]
at runtime.
So can we create array of unbounded wildcard parameterized types?
Yes. The reason being, a List<?>
is a reifiable type. And that makes sense, as there is no type associated at all. So there is nothing to loose as a result of type erasure. So, it is perfectly type-safe to create an array of such type.
List<?>[] listArr = new List<?>[10];
listArr[0] = new ArrayList<String>(); // Fine.
listArr[1] = new ArrayList<Integer>(); // Fine
Both the above case is fine, because List<?>
is super type of all the instantiation of the generic type List<E>
. So, it won't issue an ArrayStoreException at runtime. The case is same with raw types array. As raw types are also reifiable types, you can create an array List[]
.
So, it goes like, you can only create an array of reifiable types, but not non-reifiable types. Note that, in all the above cases, declaration of array is fine, it's the creation of array with new
operator, which gives issues. But, there is no point in declaring an array of those reference types, as they can't point to anything but null
(Ignoring the unbounded types).
Is there any workaround for E[]
?
Yes, you can create the array using Array#newInstance()
method:
public <E> E[] getArray(Class<E> clazz, int size) {
@SuppressWarnings("unchecked")
E[] arr = (E[]) Array.newInstance(clazz, size);
return arr;
}
Typecast is needed because that method returns an Object
. But you can be sure that it's a safe cast. So, you can even use @SuppressWarnings on that variable.
Here is the implementation of LinkedList<T>#toArray(T[])
:
public <T> T[] toArray(T[] a) {
if (a.length < size)
a = (T[])java.lang.reflect.Array.newInstance(
a.getClass().getComponentType(), size);
int i = 0;
Object[] result = a;
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item;
if (a.length > size)
a[size] = null;
return a;
}
In short, you could only create generic arrays through Array.newInstance(Class, int)
where int
is the size of the array.
Problem is that while runtime generic type is erased so new E[10]
would be equivalent to new Object[10]
.
This would be dangerous because it would be possible to put in array other data than of E
type. That is why you need to explicitly say that type you want by either
- creating Object array and cast it to
E[]
array, or - useing Array.newInstance(Class componentType, int length) to create real instance of array of type passed in
componentType
argiment.
checked :
public Constructor(Class<E> c, int length) {
elements = (E[]) Array.newInstance(c, length);
}
or unchecked :
public Constructor(int s) {
elements = new Object[s];
}
참고URL : https://stackoverflow.com/questions/18581002/how-to-create-a-generic-array
'Programing' 카테고리의 다른 글
GSON을 사용하는 Json의 Kotlin 데이터 클래스 (0) | 2020.09.21 |
---|---|
Jersey : 실제 요청 인쇄 (0) | 2020.09.21 |
UIScrollView에서 세로 스크롤 비활성화 (0) | 2020.09.20 |
Python이 C ++보다 빠르고 가볍습니까? (0) | 2020.09.20 |
유닉스 스크립트를 15 초마다 실행하는 방법은 무엇입니까? (0) | 2020.09.20 |