Java Generics가 프리미티브 유형을 지원하지 않는 이유는 무엇입니까?


236

Java의 제네릭이 클래스와 작동하지만 프리미티브 유형에서는 작동하지 않는 이유는 무엇입니까?

예를 들어, 이것은 잘 작동합니다.

List<Integer> foo = new ArrayList<Integer>();

그러나 이것은 허용되지 않습니다 :

List<int> bar = new ArrayList<int>();

1
int i = (int) 새로운 객체 (); 그래도 잘 컴파일됩니다.
Sachin Verma

답변:


243

Java의 제네릭은 완전히 컴파일 타임 구조입니다. 컴파일러는 모든 일반적인 용도를 올바른 유형으로 캐스트합니다. 이는 이전 JVM 런타임과의 호환성을 유지하기위한 것입니다.

이:

List<ClassA> list = new ArrayList<ClassA>();
list.add(new ClassA());
ClassA a = list.get(0);

(대략)으로 변합니다.

List list = new ArrayList();
list.add(new ClassA());
ClassA a = (ClassA)list.get(0);

따라서 제네릭으로 사용되는 모든 항목은 Object로 변환 가능해야하며 (이 예제에서는을 get(0)반환 Object) 기본 유형은 그렇지 않습니다. 따라서 제네릭에는 사용할 수 없습니다.


8
@DanyalAytekin-사실, Java 제네릭은 전혀 C ++ 템플릿처럼 처리되지 않습니다.
Stephen C

20
Java 컴파일러가 기본 유형을 사용하기 전에 상자에 넣을 수없는 이유는 무엇입니까? 이것이 가능할까요?
vrwim

13
@vrwim-가능할 수도 있습니다. 그러나 그것은 단지 구문 설탕 일 것입니다. 실제 문제는 박스형 프리미티브를 가진 Java 제네릭이 실제 프리미티브 유형이 사용되는 C ++ / C # 모델과 비교하여 시간과 공간에서 상대적으로 비싸다는 것입니다.
Stephen C

6
@MauganRa 그래 나는 내가 할 수 있다는 것을 안다 :) 나는 이것이 끔찍한 디자인이라는 내 입장을 견뎌야한다. 바라건대 Java 10 (또는 들었습니다) 및 고차 함수로 수정되기를 바랍니다. 그 말에 저를 인용하지 마십시오.
Ced

4
@Ced는 초보자와 전문가 모두에게 끝없이 상처를주는 것은 나쁜 디자인이라는 것에 전적으로 동의합니다
MauganRa

37

언어가 설계 한 후 그들이 년의 언어에 번호를 추가 되었기 때문에 자바에서 제네릭는 ... 적어도 부분적으로 ... 그들이하는 방식으로 작동 1 . 언어 설계자들은 기존 언어 및 Java 클래스 라이브러리와 역 호환되는 디자인을 제안함으로써 제네릭 옵션에 제약 을 받았다 .

다른 프로그래밍 언어 (예 : C ++, C #, Ada)에서는 기본 유형을 제네릭의 매개 변수 유형으로 사용할 수 있습니다. 그러나 이런 일을하는 것의 단점은 그러한 언어의 제네릭 (또는 템플릿 타입) 구현은 일반적으로 각 타입 파라미터 화를위한 제네릭 타입의 별개의 사본을 생성한다는 점입니다.


1-제네릭이 Java 1.0에 포함되지 않은 이유는 시간 압력 때문이었습니다. 그들은 웹 브라우저가 제공하는 새로운 시장 기회를 채우기 위해 Java 언어를 신속하게 출시해야한다고 생각했습니다. 제임스 고슬링은 시간이 있었다면 제네릭을 포함하고 싶었다고 말했다. 이것이 일어난다면 Java 언어는 어떤 사람의 추측 일 것입니다.


11

자바에서 제네릭은 이전 버전과의 호환성을 위해 "Type erasure"를 사용하여 구현됩니다. 모든 제네릭 형식은 런타임에 Object로 변환됩니다. 예를 들어

public class Container<T> {

    private T data;

    public T getData() {
        return data;
    }
}

런타임에 다음과 같이 볼 수 있습니다.

public class Container {

    private Object data;

    public Object getData() {
        return data;
    }
}

컴파일러는 형식 안전성을 보장하기 위해 적절한 캐스트를 제공해야합니다.

Container<Integer> val = new Container<Integer>();
Integer data = val.getData()

될 것입니다

Container val = new Container();
Integer data = (Integer) val.getData()

이제 "Object"가 런타임에 유형으로 선택되는 이유는 무엇입니까?

Answer is Object 는 모든 객체의 수퍼 클래스이며 모든 사용자 정의 객체를 나타낼 수 있습니다.

모든 프리미티브 는 " Object " 에서 상속받지 않으므로 일반 유형으로 사용할 수 없습니다.

참고 : 프로젝트 Valhalla는 위의 문제를 해결하려고합니다.


적절한 명칭을 위해 1을 더합니다.
Drazen Bjelovuk

7

컬렉션은에서 파생 된 유형을 요구하도록 정의됩니다 java.lang.Object. 기본 유형은 단순히 그렇게하지 않습니다.


26
여기서 질문은 "왜"라고 생각합니다. 제네릭에 개체가 필요한 이유는 무엇입니까? 컨센서스는 디자인 선택이 적고 이전 버전과의 호환성에 더 많은 관심을 가지고있는 것으로 보입니다. 내 눈에, 제네릭이 프리미티브를 처리 할 수 ​​없다면, 그것은 기능적 결함이다. 기본적으로 프리미티브와 관련된 모든 것은 각 프리미티브에 대해 작성되어야합니다 .Comparator <t, t> 대신 Integer.compare (int a, int b), Byte.compare (byte a, byte b) 등이 있습니다. 그것은 해결책이 아닙니다!
John P

1
예, 기본 유형에 대한 제네릭은 필수 기능입니다. 다음은 제안에 대한 링크입니다 openjdk.java.net/jeps/218
crow

5

당으로 자바 문서 , 제네릭 형식의 변수는 참조 유형이 아닌 기본 유형에 인스턴스를 생성 할 수 있습니다.

이것은 프로젝트 Valhalla 에서 Java 10으로 제공됩니다 .

에서 브라이언 게츠의 종이 전문화의 주

제네릭이 프리미티브에 대해 지원되지 않은 이유에 대한 훌륭한 설명 이 있습니다 . 그리고 향후 Java 릴리스에서 어떻게 구현 될 것인가 .

모든 참조 인스턴스화에 대해 하나의 클래스를 생성하고 기본 인스턴스화를 지원하지 않는 Java의 현재 지우기 구현. (이것은 동종 변환이며 Java의 제네릭이 참조 유형에 대해서만 범위를 지정할 수 있다는 제한은 참조 유형과 원시 유형에 대한 조작에 다른 바이트 코드를 사용하는 JVM의 바이트 코드 세트와 관련하여 동종 변환의 제한에서 비롯됩니다.) 그러나 Java에서 지워진 제네릭은 동작 매개 변수 (일반적인 방법)와 데이터 파라 메트릭 (제네릭 형식의 원시 및 와일드 카드 인스턴스화)을 모두 제공합니다.

...

일반 유형 변수가 바이트 코드에 통합 될 때 경계에서 지워지는 동종 변환 전략이 선택되었습니다. 이는 클래스가 일반인지 여부에 관계없이 동일한 이름을 가진 단일 클래스로 컴파일되며 멤버 서명이 동일한 것을 의미합니다. 컴파일시 타입 안전이 검증되고 일반 타입 시스템에 의해 런타임이 영향을받지 않습니다. 결과적으로 Object는 사용 가능한 가장 일반적인 유형이며 기본 유형으로 확장되지 않기 때문에 제네릭이 참조 유형에서만 작동 할 수 있다는 제한을가했습니다.


0

객체를 만들 때 type 매개 변수를 프리미티브 유형으로 대체 할 수 없습니다.이 제한의 이유는 컴파일러 구현 문제입니다. 기본 유형에는 가상 머신 스택에로드 및 저장하기위한 고유 한 바이트 코드 명령이 있습니다. 따라서 원시 제네릭을 이러한 별도의 바이트 코드 경로로 컴파일하는 것은 불가능하지 않지만 컴파일러를 복잡하게 만듭니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.