[java] Java에서 일반 배열 유형을 만들 수없는 이유는 무엇입니까?

Java가 우리를 허용하지 않는 이유는 무엇입니까?

private T[] elements = new T[initialCapacity];

.NET에서 런타임에 다른 크기를 가질 수있는 값 유형이 있기 때문에 .NET이 그렇게 할 수 없다는 것을 이해할 수 있었지만 Java에서는 모든 종류의 T가 객체 참조이므로 동일한 크기 ( 틀 렸으면 말해줘).

이유가 무엇입니까?



답변

제네릭과 달리 Java의 배열에는 런타임에 구성 요소 유형에 대한 정보가 포함되어 있기 때문입니다. 따라서 배열을 만들 때 구성 요소 유형을 알아야합니다. T런타임에 무엇인지 모르므로 배열을 만들 수 없습니다.


답변

인용문:

제네릭 형식의 배열은 소리가 나지 않으므로 사용할 수 없습니다. 문제는 정적으로 건전하지 않지만 동적으로 확인되는 Java 배열과 정적으로 건전하고 동적으로 확인되지 않은 제네릭을 사용하여 상호 작용하기 때문입니다. 허점을 활용하는 방법은 다음과 같습니다.

class Box<T> {
    final T x;
    Box(T x) {
        this.x = x;
    }
}

class Loophole {
    public static void main(String[] args) {
        Box<String>[] bsa = new Box<String>[3];
        Object[] oa = bsa;
        oa[0] = new Box<Integer>(3); // error not caught by array store check
        String s = bsa[0].x; // BOOM!
    }
}

Tiger에서는 거부 된 정적 안전 배열 (일명 Variance) 버트를 사용하여이 문제를 해결하도록 제안했습니다.

사후

(나는 그것이 Neal Gafter 라고 생각 하지만 확실하지 않습니다)

컨텍스트에서 참조하십시오 : http://forums.sun.com/thread.jspa?threadID=457033&forumID=316


답변

적절한 솔루션을 제공하지 않으면 더 나쁜 IMHO로 끝납니다.

일반적인 해결 방법은 다음과 같습니다.

T[] ts = new T[n];

(T가 다른 클래스가 아닌 Object를 확장한다고 가정)

T[] ts = (T[]) new Object[n];

나는 첫 번째 예를 선호하지만 더 많은 학업 유형이 두 번째 예를 선호하거나 생각하지 않는 것을 선호합니다.

Object []를 사용할 수없는 이유의 대부분의 예는 List 또는 Collection (지원되는)에 동일하게 적용되므로 매우 나쁜 인수로 간주합니다.

참고 : 이것이 Collections 라이브러리 자체가 경고없이 컴파일되지 않는 이유 중 하나입니다. 이 사용 사례를 경고없이 지원할 수없는 경우 제네릭 모델 IMHO에서 근본적으로 문제가 발생합니다.


답변

배열은 공변량입니다

배열은 공변량이라고하며 기본적으로 Java의 하위 유형 지정 규칙에서 유형의 배열은 유형의 T[]요소 T또는의 하위 유형을 포함 할 수 있습니다 T. 예를 들어

Number[] numbers = new Number[3];
numbers[0] = newInteger(10);
numbers[1] = newDouble(3.14);
numbers[2] = newByte(0);

그러나 Java의 하위 유형 지정 규칙은 또한 S[]의 하위 유형 인 경우 배열이 배열 의 하위 유형이라고 T[]명시 하므로 다음과 같은 것도 유효합니다.ST

Integer[] myInts = {1,2,3,4};
Number[] myNumber = myInts;

Java의 하위 유형 지정 규칙에 따라 배열 Integer[]은 배열 의 하위 유형이므로Number[] 정수는 숫자의 하위 유형이므로 의 하위 유형입니다.

그러나이 하위 유형 규칙은 흥미로운 질문으로 이어질 수 있습니다. 우리가 이것을 시도하면 어떻게 될까요?

myNumber[0] = 3.14; //attempt of heap pollution

이 마지막 줄은 잘 컴파일되지만이 코드를 실행하면 ArrayStoreException 하면 정수 배열에 double을 넣으려고하기 때문에 있습니다. 여기서 Number 참조를 통해 배열에 액세스한다는 사실은 중요하지 않습니다. 중요한 것은 배열이 정수 배열이라는 것입니다.

이는 컴파일러를 속일 수는 있지만 런타임 유형 시스템을 속일 수는 없다는 것을 의미합니다. 배열은 우리가 reifiable 타입이라고 부르기 때문입니다. 이는 런타임시 Java가이 배열이 실제로 유형의 참조를 통해 액세스되는 정수 배열로 인스턴스화되었음을 알고 있음을 의미합니다.Number[] .

보시다시피, 하나는 객체의 실제 유형이고 다른 하나는 객체에 액세스하는 데 사용하는 참조의 유형입니다.

자바 제네릭의 문제점

이제 Java에서 제네릭 형식의 문제점은 코드 컴파일이 완료된 후 형식 매개 변수의 형식 정보가 컴파일러에 의해 삭제된다는 것입니다. 따라서이 유형 정보는 런타임에 사용할 수 없습니다. 이 과정을 유형 삭제 라고 합니다 . Java에서 이와 같은 제네릭을 구현하는 데는 합당한 이유가 있지만, 이는 오랜 이야기이며 기존 코드와 바이너리 호환성과 관련이 있습니다.

여기서 중요한 점은 런타임에 유형 정보가 없기 때문에 힙 오염을 저 지르지 않도록 보장 할 방법이 없다는 것입니다.

다음과 같은 안전하지 않은 코드를 살펴 보겠습니다.

List<Integer> myInts = newArrayList<Integer>();
myInts.add(1);
myInts.add(2);
List<Number> myNums = myInts; //compiler error
myNums.add(3.14); //heap polution

Java 컴파일러가이를 수행하지 못하게하는 경우 런타임 유형 시스템은 런타임시이 목록이 정수 목록으로 만 가정 된 방법이 없기 때문에 중지 할 수 없습니다. Java 런타임은 정수만 포함해야 할 때이 목록에 원하는 것을 넣을 수 있습니다. 생성 될 때 정수 목록으로 선언 되었기 때문입니다. 그렇기 때문에 컴파일러는 라인 번호 4를 안전하지 않기 때문에 거부합니다.

따라서 Java 디자이너는 컴파일러를 속일 수 없도록했습니다. 우리가 컴파일러를 속일 수 없다면 (배열로 할 수있는 것처럼) 런타임 타입 시스템을 속일 수 없습니다.

따라서 런타임에 일반 유형의 실제 특성을 판별 할 수 없으므로 일반 유형은 수정할 수 없다고 말합니다.

이 답변의 일부를 건너 뛰었습니다.
https://dzone.com/articles/covariance-and-contravariance


답변

이것이 불가능한 이유는 Java가 컴파일러 레벨에서 순수하게 Generics를 구현하고 각 클래스마다 하나의 클래스 파일 만 생성되기 때문입니다. 이것을 유형 소 거라고합니다. 합니다.

런타임시 컴파일 된 클래스는 모든 사용을 동일한 바이트 코드로 처리해야합니다. 따라서 new T[capacity]어떤 유형을 인스턴스화해야하는지 전혀 모를 것입니다.


답변

대답은 이미 제공되었지만 이미 인스턴스가 T 인 경우 다음을 수행 할 수 있습니다.

T t; //Assuming you already have this object instantiated or given by parameter.
int length;
T[] ts = (T[]) Array.newInstance(t.getClass(), length);

희망, 나는 도울 수 있었다, Ferdi265


답변

주된 이유는 Java의 배열이 공변량이기 때문입니다.

여기에 좋은 개요가 있습니다 .