2016-06-27 11 views
13

Java는 Java 5에서 제네릭을 사용하여 유형 삭제 기능을 도입했기 때문에 이전 버전의 Java에서도 작동합니다. 그것은 호환성을위한 절충안이었습니다. 그 이후 호환성을 잃어 버렸습니다. [1][2] [3] - 바이트 코드는 이전 버전이 아닌 JVM의 이후 버전에서 실행될 수 있습니다. 이것은 가능한 선택의 여지가없는 것처럼 보입니다. 우리는 형식 정보를 잃어 버렸고 이전 버전의 JVM의 최신 버전 용으로 컴파일 된 바이트 코드를 실행할 수 없습니다. 어떻게 된 거예요?왜 다음 JVM에서 유형 삭제를 제거하지 않습니까?

구체적으로 필자는 JVM의 다음 버전에서 형식 지우기를 제거 할 수없는 기술적 이유가 있는지 묻습니다 (이전 릴리스와 마찬가지로 바이트 코드가 마지막 버전에서 실행되지 않을 것이라고 가정 함). 어쨌든).

[3] : 유형 지우기는 실제로 그것을 좋아하는 사람들을 위해 retrolambda과 비슷한 방식으로 백 포트 될 수 있습니다.

편집 : 역방향과 전달 호환성의 정의에 대한 논의는 질문을 모호하게 만든다고 생각합니다.

+6

하위 호환성이 없어지지 않았습니다. 오래된 Java 1.1 프로그램은 링크 된 게시물이 말하는 JRE 8 VM에서 원활하게 실행됩니다. – Tunaki

+0

@Tunaki 내가 틀렸다고 정정 하긴하지만 포워드와의 호환성이 있습니다. 코드는 향후 JVM에서 실행될 것입니다. JVM 버전 X에서 유형 삭제를 제거하면 JVM X + 1에서 제거되므로 코드가 여전히 실행됩니다. – Prime

+1

정확하지는 않습니다. http://stackoverflow.com/questions/4692626/is-jdk-upward-or-backward-compatible – Tunaki

답변

9

향후 값 유형에 대한 특수 구현을 가능하게하기 위해 지우침이 project valhalla으로 제거 될 예정입니다.

더 정확하게 말하면, erasure는 제네릭의 유형 전문화가 없다는 것을 의미합니다. 그리고 valhalla는 프리미티브에 대한 전문화를 도입 할 것입니다. 유형의 삭제는 JVM

성능의 다음 버전에서 제거 될 수없는 이유를 기술적 이유가있는 경우

는 특히 내가 부탁 해요. 모든 제네릭 유형, 인스턴스 또는 생성 된 클래스의 조합에 대해 특수화 된 코드를 생성 할 필요가 없기 때문에 유형 태그, 다형성 인라인 캐시 및 런타임 유형 검사 (컴파일러에서 생성 된 instanceof 검사)를 수행 할 필요가 없으며 여전히 대부분을 얻습니다. 컴파일 타임 검사를 통한 유형 안전성.

물론 단점도 많이 있지만, 이미 상충 관계가 있으며, JVM 개발자가 그 절충안을 바꾸도록 동기를 부여 할 수있는 질문이 있습니다.

또한 호환성 문제가있을 수 있습니다. 유형 제약 조건이 적용될 경우 손상 될 유형 삭제에 의존하여 일반 컬렉션을 남용하는 체크되지 않은 캐스트를 수행하는 코드가있을 수 있습니다.

6

이전 버전과의 호환성에 대한 이해가 잘못되었습니다. 새로운 JVM의도 새로운 코드를 제대로 이전 라이브러리 코드를 실행할 수 및 변경이 될 수 있도록

원하는 목표입니다. 이를 통해 사용자는 코드가 작성된 것보다 더 많은 새 버전으로도 Java 버전을 안정적으로 업그레이드 할 수 있습니다.

+0

유형 삭제를 제거하면 어떻게 되나요? 이전 코드는 유형 정보에 의존하지 않으며 (존재하지 않기 때문에) 새 코드는 새 JVM에서만 실행됩니다. 누구나 원활하게 업그레이드 할 수 있습니다. – Prime

+1

@Prime - 그렇지 않습니다. 그러나 당신은 >> 진짜 << 이전 호환성을 버리기위한 정당화로서 역 호환성의 가짜 아이디어를 사용합니다. 또한 "모두가 완벽하게 업그레이드 할 수있다"는 대담한 주장은 훨씬 더 정당화가 필요합니다. 나는 그것을 믿지 않는다. –

+0

@StephenC type erasure는 빼기입니다. 프로그램에서 형식 정보가 지워 졌다고 가정하면 클래스 경로에 java.util.List [Object]가있는 한 제대로 실행됩니다. 더 많은 유형 정보를 추가하는 것은 전문화를 추가하는 것과 같은 것을 의미 할 수 있으므로 JVM은 예를 들어 java.util.[Integer]를 나열하고 런타임 유형 검사를 피할 수 있습니다. 구체화 된 유형을 사용하여 런타임에이 정보를 얻을 수 있습니다 (스칼라와 마찬가지로 http://docs.scala-lang.org/overviews/reflection/typetags-manifests.html 참조). 컴파일시에 항상 정보를 얻을 수있는 방법처럼 ... – Prime

12

유형 삭제는 사용자가 켜거나 끌 수있는 단순한 바이트 코드 이상의 기능입니다.

전체 런타임 환경의 작동 방식에 영향을줍니다. 제네릭 클래스의 모든 인스턴스의 제네릭 형식을 쿼리 할 수있게하려면 런타임에 해당하는 메타 정보가 제네릭 클래스의 각 개체 인스턴스화에 대해 만들어 지도록 함을 의미합니다.당신은 세 개의 객체를 생성하지 않는 new ArrayList<String>(); new ArrayList<Number>(); new ArrayList<Object>()을 작성하는 경우가 이전에 존재하지 않은 경우

, 당신은 잠재적으로, 세 개의 추가 메타 유형, ArrayList<String>, ArrayList<Number>을 반영하는 개체 및 ArrayList<Object>을 만들 수 있습니다.

대표적인 응용 프로그램에서 사용되는 다양한 List 서명이 있다고 가정합니다. 대부분이 그러한 Reflection의 가용성이 필요한 곳에서는 사용되지 않았습니다 (이 기능이 없기 때문에 현재 , 그들 모두는 그런 반사없이 일합니다.)

물론 1,000 개의 다른 일반 목록 유형에는 천개의 다른 일반 반복기 유형, 천개의 스플 리터레이터 및 스트림 구현을 암시하며 구현의 내부 클래스도 계산하지 않습니다.

그리고 심지어 이것은 객체 할당이없는 장소에서 영향을받습니다. Collections.emptyList(), Function.identity() 또는 Comparator.naturalOrder() 등은 호출 할 때마다 동일한 인스턴스를 반환합니다. Particalarar가 캡쳐 된 일반 유형을 반영 할 수 있도록 조사 할 수 있다고 주장하면 더 이상 작동하지 않습니다. 당신이

List<String> list=Collections.emptyList(); 
List<Number> list=Collections.emptyList(); 

를 작성하는 경우 그래서 당신은 그들 각각의 getClass() 또는 미래 동등한의 다른보고, 두 가지 인스턴스를받을 수있을 것입니다.


은이 능력을하고자하는 사람들이 반사적으로 하나 개의 특정 매개 변수는 실제로 두 개 또는 세 가지 유형 중 하나인지를 알 수 있다면 좋을 것 그들의 특정한 방법에 좁은 관점을 가지고 보이지만, 수천 개의 제네릭 클래스에 대한 잠재적 인 수백 또는 수천 개의 일반적인 인스턴스화에 대한 메타 정보를 운반하는 데 대한 무게를 생각하지 마십시오.

여기는 우리가 얻은 것, 즉 반사 형을 통해 발견 된 정보로 인해 코드의 동작을 변경하는 의심스러운 코딩 스타일을 지원하는 능력에 대해 묻는 것입니다.


대답 지금까지 오직 소거 형을 제거 쉽게 양태를 어드레싱 욕구는 실제 인스턴스의 유형 내성하다. 실제 인스턴스에는 구체적인 유형이 있으며이를보고 할 수 있습니다. user the8472this comment에서 언급했듯이 유형 삭제 제거 요구는 (T)으로 캐스팅하거나 new T[]을 통해 배열을 만들거나 T.class을 통해 유형 변수 유형에 액세스하려는 바람을 종종 나타냅니다.

이것은 진정한 악몽을 불러올 것입니다. 유형 변수는 실제 인스턴스의 실제 유형과 다른 짐승입니다. 유형 변수는 예 : ? extends Comparator<? super Number>을 사용하여 하나의 이름을 지정하십시오. 필요한 메타 정보를 제공하는 것은 객체 할당이 훨씬 더 비싸지 않을뿐만 아니라 모든 메소드 호출이 이러한 추가 비용을 부과 할 수 있다는 것을 의미합니다. 실제 클래스와 일반 클래스의 조합에 대해서만 이야기 할뿐만 아니라 또한 와일드 카드로 조합 된 모든 조합 (중첩 된 제네릭 유형조차도)

유형 매개 변수의 실제 유형은 다른 유형 매개 변수를 참조 할 수도 있으므로 형식 검사를 매우 복잡한 프로세스로 전환 할 수 있습니다. 이러한 유형 검사는 생성을 허용하는 경우 모든 유형 변환에 대해 반복 할 필요가 없을뿐만 아니라 그 중에서 하나의 배열은 모든 저장 작업을 반복해야합니다.

성능 문제 외에도 복잡성으로 인해 또 다른 문제가 발생합니다. javac의 버그 추적 목록이나 Stackoverflow 관련 질문을 살펴보면 프로세스가 복잡 할뿐만 아니라 오류가 발생하기 쉽다는 것을 알 수 있습니다. 현재 javac의 모든 부 버전에는 일반 유형 서명 일치와 관련된 변경 사항 및 수정 사항이 포함되어있어 수락 또는 거부 될 내용에 영향을줍니다. 변수, 배정 또는 배열 저장소와 같은 본질적인 JVM 작업이 이러한 복잡성의 희생물이되기를 원하지 않으며, 모든 버전에서 합법적인지 또는 아닌지에 대한 아이디어가 다르거 나 갑자기 무엇을 거부했는지 알 수 없습니다. javac 불일치 규칙으로 인해 컴파일 시간.

+2

리플렉션을 통해서만 사용된다고 가정합니다. 그러나 지워지지 않은 generics wouild는 클래스의 타입 변수 (예 : T)가 실제 일 것이라는 것을 의미한다. 즉,'(T)를 통한 캐스팅은 실제 캐스팅이 될 것이고 fail-fast 동작을 제공 할 것이다.'new T []는 배열'T.class'는 클래스 객체를 줄 수 있습니다. 또한 메타 데이터 객체를 만들 필요가 있지만 클래스의 수에 비해 일정한 요소 일 수 있습니다. 일반적인 서명은 궁극적으로 클래스 당 한정된 양만있는 할당 호출에 의해 유도되기 때문입니다. – the8472

+2

@ the8472 : 나는 쉬운 부분에 초점을 맞췄다. 당신이 묘사하고있는 것이 상황을 훨씬 나쁘게 만든다. 일반적인 * 메소드 *가 있기 때문에, 설명하는 기능은 객체 할당 사이트뿐만 아니라 각 호출이 그 오버 헤드를 견딜 수 있음을 암시합니다. 나는 관련 문제의 일부를 해결하기 위해 내 대답을 보냈다. 물론 호출 사이트의 수는 유한하지만 우주의 별의 수 또한 유한 일 수 있습니다 ... – Holger

+1

유형 경계와 콘크리트 유형을 구별하는 좋은 점은 복잡성의 또 다른 원인을 추가합니다. – the8472

1

특히 이전 버전과 마찬가지로 유형 삭제가 JVM의 다음 버전에서 제거 될 수없는 기술적 인 이유가 있는지 묻는 것입니다 (이전 릴리스와 마찬가지로 바이트 코드가 어쨌든 마지막 버전).

이전 버전과의 호환성이 손상 될 수 있습니다. (원래의 질문과 관련한 신화적인 종류가 아닙니다.)

예를 들어 기술적 이유는 역 호환성이 Java 언어/플랫폼의 가장 중요한 속성 중 하나이기 때문입니다. 오라클과 오라클의 유료 고객 관점입니다.

유형 삭제가 작동을 멈 추면 지난 20 년 동안 많은 기업이 만든 Java 기반 소프트웨어에 수십억 달러의 투자가 갑자기 생길 수 있습니다.


직접 물어보십시오. 왜 Sun/Oracle은 Thread.stop()을 제거하지 않았습니까?