Java 유형 삭제의 이점은 무엇입니까?


98

나는 오늘 다음과 같은 트윗을 읽었습니다 .

Java 사용자가 유형 삭제에 대해 불평하는 것은 재미 있습니다. 이것은 Java가 옳은 유일한 방법이며 잘못된 모든 것은 무시합니다.

따라서 내 질문은 다음과 같습니다.

Java의 유형 삭제로 인한 이점이 있습니까? 이전 버전과의 호환성 및 런타임 성능에 대한 JVM 구현 환경 설정 외에 제공하는 기술 또는 프로그래밍 스타일 이점은 무엇입니까?


6
질문이 "의견 기반"으로 종료되는 것을 원하지 않는 경우에는 다르게 질문해야합니다. (예 : "유형 삭제의 기술적 이점은 무엇입니까"). 더 좋은 방법은 트위터에게 그가 정말로 말하는 것을 물어 보는 것입니다.
Stephen C

3
"이것이 좋은 이유"를 제거하십시오. "좋은 것"은 특허 적으로 주관적인 특성이며, 유형 삭제 좋은 것입니다 ... 논쟁의 여지가 있습니다.
Stephen C

3
이 사이트가 트위터에 게시 된 모든 것에 대한 질문으로 변질된다면 하나님은 우리를 도와 주 십니다. 적어도 당신의 질문에 대해 평판이 좋은 출처를 인용하십시오.
Marquis of Lorne

21
질문이 "신뢰할 수있는 출처"에서 제공되어야하는 이유는 무엇입니까? 그것은 단지 어리석은 일입니다.
vertti 2014 년

4
대부분의 문제는 질문자가 특정 소프트웨어 / 언어 / 무엇이든 문제를 소유하고 있다는 점을 제외하고는 어떤 출처에서도 발생하지 않습니다. 다행스럽게도 이러한 사람들을 돕는 데 시간을 "낭비"하는 많은 전문가가 있습니다.
vertti 2014 년

답변:


90

유형 삭제가 좋다

사실을 고수합시다

지금까지 많은 답변이 트위터 사용자와 지나치게 관련되어 있습니다. 메신저가 아닌 메시지에 계속 집중하는 것이 좋습니다. 지금까지 언급 한 발췌문만으로도 상당히 일관된 메시지가 있습니다.

Java 사용자가 유형 삭제에 대해 불평하는 것은 재미 있습니다. 이것은 Java가 옳은 유일한 방법이며 잘못된 모든 것은 무시합니다.

나는 엄청난 혜택 (예 : 파라 메트릭)과 비용이 없습니다 (권장 비용은 상상의 한계입니다).

새로운 T는 깨진 프로그램입니다. "모든 명제는 참되다"는 주장과 동형이다. 나는 이것에 크지 않습니다.

목표 : 합리적인 프로그램

이 트윗은 우리가 기계가 어떤 일을 하게 할 수 있는지에 관심이없는 관점을 반영 하지만, 기계가 우리가 실제로 원하는 것을 할 것이라고 추론 할 수 있는지에 대한 더 많은 관점을 반영합니다 . 좋은 추론은 증거입니다. 증명은 형식 표기법이나 덜 형식적인 것으로 지정 될 수 있습니다. 사양 언어에 관계없이 명확하고 엄격해야합니다. 비공식적 인 사양은 올바르게 구조화하는 것이 불가능하지는 않지만 실제 프로그래밍에서 종종 결함이 있습니다. 우리는 비공식적 추론에 대한 문제를 보완하기 위해 자동화 및 탐색 적 테스트와 같은 수정 작업을 수행합니다. 이것은 테스트가 본질적으로 나쁜 생각이라는 말은 아니지만 인용 된 트위터 사용자는 훨씬 더 나은 방법이 있다고 제안하고 있습니다.

따라서 우리의 목표는 기계가 실제로 프로그램을 실행하는 방법과 일치하는 방식으로 명확하고 엄격하게 추론 할 수있는 올바른 프로그램을 갖는 것입니다. 그러나 이것이 유일한 목표는 아닙니다. 또한 논리가 어느 정도의 표현력을 갖기를 원합니다. 예를 들어, 명제 논리로 표현할 수있는 것은 너무 많습니다. 1 차 논리와 같은 것에서 보편적 (∀) 및 실존 적 (∃) 정량화를 갖는 것은 좋습니다.

추론을 위해 유형 시스템 사용

이러한 목표는 유형 시스템에 의해 매우 잘 처리 될 수 있습니다. 이것은 Curry-Howard 서신 때문에 특히 분명 합니다 . 이 일치는 종종 다음과 같은 비유로 표현됩니다. 유형은 프로그램에 대한 정리와 증명에 대한 것입니다.

이 서신은 다소 심오합니다. 논리식을 취하고 유형에 대한 대응을 통해 번역 할 수 있습니다. 그런 다음 컴파일되는 동일한 유형 시그니처를 가진 프로그램이있는 경우 논리적 표현이 보편적으로 참 (타우 톨 로지)임을 입증했습니다. 이는 통신이 양방향이기 때문입니다. 유형 / 프로그램과 정리 / 증명 세계 간의 변환은 기계적이며 많은 경우 자동화 될 수 있습니다.

Curry-Howard는 우리가 프로그램의 사양으로하고 싶은 일을 잘 처리합니다.

유형 시스템이 Java에서 유용합니까?

Curry-Howard를 이해 했음에도 불구하고 어떤 사람들은 유형 시스템의 가치를 무시하기 쉽다고 생각합니다.

  1. 작업하기가 매우 어렵습니다.
  2. (Curry-Howard를 통해) 표현력이 제한된 논리에 해당
  3. (시스템이 "약함"또는 "강함"으로 특성화 됨)

첫 번째 요점과 관련하여 아마도 IDE는 Java의 유형 시스템을 작업하기에 충분히 쉽게 만들 수 있습니다 (매우 주관적입니다).

두 번째 요점과 관련하여 Java는 거의 1 차 논리에 해당합니다. 제네릭은 보편적 정량화와 동등한 유형 시스템을 사용합니다. 불행히도 와일드 카드는 실존 적 정량화의 극히 일부만을 제공합니다. 그러나 보편적 인 정량화는 꽤 좋은 시작입니다. A는 완전히 구속되지 않기 때문에 가능한 모든 목록에 List<A>대해 보편적으로 작동 하는 함수라고 말할 수 있다는 것이 좋습니다. 이것은 트위터 사용자가 "매개 변수"와 관련하여 말하는 내용으로 이어집니다.

파라 메트릭에 대해 자주 인용되는 논문은 무료로 Philip Wadler의 정리입니다! . 이 논문에서 흥미로운 점은 타입 시그니처만으로도 매우 흥미로운 불변성을 증명할 수 있다는 것입니다. 이러한 불변성에 대한 자동화 된 테스트를 작성한다면 우리는 많은 시간을 낭비하게 될 것입니다. 예를 들어 for List<A>,flatten

<A> List<A> flatten(List<List<A>> nestedLists);

우리는 그것을 추론 할 수 있습니다

flatten(nestedList.map(l -> l.map(any_function)))
     flatten(nestList).map(any_function)

이것은 간단한 예이며 비공식적으로 추론 할 수 있지만 형식 시스템에서 공식적으로 무료로 그러한 증명을 얻고 컴파일러에서 확인하면 더 좋습니다.

지우지 않으면 남용으로 이어질 수 있습니다.

언어 구현의 관점에서 Java의 제네릭 (범용 유형에 해당)은 프로그램이 수행하는 작업에 대한 증명을 얻는 데 사용되는 매개 변수에 매우 많이 사용됩니다. 이것은 언급 된 세 번째 문제에 도달합니다. 이러한 모든 증거와 정확성을 얻으려면 결함없이 구현 된 사운드 유형 시스템이 필요합니다. 자바에는 우리의 추론을 깨뜨릴 수있는 몇 가지 언어 기능이 있습니다. 여기에는 다음이 포함되지만 이에 국한되지는 않습니다.

  • 외부 시스템의 부작용
  • 반사

삭제되지 않은 제네릭은 여러면에서 반사와 관련이 있습니다. 지워지지 않으면 알고리즘을 설계하는 데 사용할 수있는 구현과 함께 전달되는 런타임 정보가 있습니다. 이것이 의미하는 바는 정적으로 프로그램에 대해 추론 할 때 전체 그림을 가지고 있지 않다는 것입니다. 반사는 우리가 정적으로 추론하는 모든 증거의 정확성을 심각하게 위협합니다. 우연의 일치가 아닙니다. 반사는 또한 다양한 까다로운 결함으로 이어집니다.

그렇다면 지워지지 않은 제네릭이 "유용"할 수있는 방법은 무엇입니까? 트윗에 언급 된 사용법을 고려해 보겠습니다.

<T> T broken { return new T(); }

T에 인수가없는 생성자가 없으면 어떻게됩니까? 일부 언어에서 얻는 것은 null입니다. 또는 null 값을 건너 뛰고 곧바로 예외를 발생시킵니다 (널 값이 어쨌든 발생하는 것 같습니다). 우리의 언어는 튜링이 완전하기 때문에 어떤 호출이 broken인수가없는 생성자가있는 "안전한"유형을 포함하고 어떤 유형이 그렇지 않은지에 대해 추론 할 수 없습니다. 우리 프로그램이 보편적으로 작동한다는 확신을 잃었습니다.

지우는 것은 우리가 추론했다는 것을 의미합니다 (그러니 지우자)

따라서 우리 프로그램에 대해 추론하고 싶다면 추론을 강력하게 위협하는 언어 기능을 사용하지 않는 것이 좋습니다. 그런 다음 런타임에 유형을 삭제하지 않는 이유는 무엇입니까? 필요하지 않습니다. 캐스트가 실패하지 않거나 호출시 메소드가 누락 될 수 있다는 만족감으로 효율성과 단순성을 얻을 수 있습니다.

지우는 것은 추론을 장려합니다.


7
내가이 응답을 뭉치려고 할 때 다른 사람들은 비슷한 대답으로 응답했습니다. 하지만 이렇게 많이 써서 버리지 않고 게시하기로 결정했습니다.
Sukant Hajra

32
그와 관련하여 유형 삭제는 이전 버전과의 호환성을 깨지 않고 Java로 기능을 가져 오려는 절반의 시도였습니다. 그것은 힘이 아닙니다. 매개 변수없는 생성자가없는 객체를 인스턴스화하려는 것이 주요 관심사라면 올바른 대답은 언어 기능을 손상시키지 않고 "매개 변수없는 생성자가있는 유형"의 제약 조건을 허용하는 것입니다.
Basic

5
기본적으로, 존경심으로, 당신은 응집력있는 논쟁을하지 않습니다. 당신은 단순히 프로그램에 대한 추론을위한 강력한 도구로서 컴파일 타임 증명의 가치를 완전히 무시하면서 삭제가 "절대"라고 주장하는 것입니다. 또한 이러한 이점은 Java가 제네릭을 구현하기 위해 취한 비뚤어진 경로 및 동기와 완전히 직교합니다. 또한 이러한 이점은 삭제를 훨씬 더 잘 구현 한 언어에 적용됩니다.
Sukant Hajra

44
여기에 제시된 주장은 삭제에 반대하는 것이 아니라 반영에 반대하는 것입니다 (일반적으로 런타임 유형 검사). 기본적으로 반영이 나쁘다고 주장하므로 삭제가 잘 작동하지 않는다는 사실이 좋습니다. (많은 사람들이 동의하지 않지만) 그 자체로 유효한 점입니다. 그러나 Java에는 이미 리플렉션 / 런타임 검사가 있고 사라지지 않았고 사라지지 않기 때문에 문맥 상별로 의미가 없습니다. 그래서 언어의 기존 비 사용되지 않는 부분이 재생되지 않는 구조를 가지고하는 것입니다 제한, 당신이 그것을 보면 아무리.
Pavel Minaev

10
귀하의 답변은 단순히 주제에서 벗어났습니다. 추상적 인 개념으로서 유형 삭제가 유형 시스템 설계에 일반적으로 유용한 이유에 대한 장황한 (그 자체로는 흥미롭지 만) 설명을 입력하지만 주제는 "유형 삭제"라는 레이블이 붙은 Java Generics의 특정 특성의 이점입니다. "는 설명하는 개념과 표면적으로 만 닮았으며 흥미로운 불변성을 제공하고 불합리한 제약을 도입하는 데 완전히 실패했습니다.
마르코 Topolnik

41

유형은 컴파일러가 프로그램의 정확성을 검사 할 수있는 방식으로 프로그램을 작성하는 데 사용되는 구조입니다. 유형은 값에 대한 제안입니다. 컴파일러는이 제안이 참인지 확인합니다.

프로그램을 실행하는 동안 유형 정보가 필요하지 않아야합니다. 이것은 컴파일러에 의해 이미 확인되었습니다. 컴파일러는 코드에서 최적화를 수행하기 위해이 정보를 자유롭게 버릴 수 있어야합니다. 코드를 더 빠르게 실행하고, 더 작은 바이너리를 생성하는 등의 작업을 수행 할 수 있습니다.

Java는 런타임시 유형 정보 (reflection, instanceof 등)를 쿼리 할 수 ​​있도록하여 정적 유형을 중단합니다.이를 통해 정적으로 확인할 수없는 프로그램을 구성 할 수 있습니다. 이는 유형 시스템을 우회합니다. 또한 정적 최적화 기회를 놓칩니다.

유형 매개 변수가 지워진다는 사실은 이러한 잘못된 프로그램의 일부 인스턴스가 생성되는 것을 방지하지만 더 많은 유형 정보가 지워지고 기능의 반영 및 인스턴스가 제거되면 더 많은 잘못된 프로그램이 허용되지 않습니다.

삭제는 데이터 유형의 "매개 변수"속성을 유지하는 데 중요합니다. 구성 요소 유형 T에 대해 매개 변수화 된 "List"유형이 있다고 가정합니다. 즉, List <T>입니다. 이 유형은이 List 유형이 모든 유형 T에 대해 동일하게 작동한다는 명제입니다. T가 추상적이고 제한되지 않은 유형 매개 변수라는 사실은 우리가이 유형에 대해 전혀 알지 못하므로 T의 특수한 경우에 대해 특별한 작업을 수행 할 수 없음을 의미합니다.

예를 들어 목록 xs = asList ( "3")가 있다고 가정합니다. 요소를 추가합니다 : xs.add ( "q"). 나는 [ "3", "q"]로 끝납니다. 이것은 파라 메트릭이므로 List xs = asList (7); xs.add (8)은 [7,8]로 끝납니다. 유형에서 String에 대해 한 가지 작업을 수행하지 않고 Int에 대해 한 가지 작업을 수행하지 않는다는 것을 알고 있습니다.

또한 List.add 함수는 T 값을 허공에서 만들 수 없다는 것을 알고 있습니다. 내 asList ( "3")에 "7"이 추가 된 경우 가능한 대답은 "3"과 "7"값으로 구성된다는 것을 알고 있습니다. 함수가 목록을 구성 할 수 없기 때문에 "2"또는 "z"가 목록에 추가 될 가능성이 없습니다. 이러한 다른 값 중 어느 것도 추가 할 수 없으며 매개 변수는 이러한 잘못된 프로그램이 생성되는 것을 방지합니다.

기본적으로 삭제는 매개 변수를 위반하는 수단을 방지하여 정적 타이핑의 목표 인 잘못된 프로그램의 가능성을 제거합니다.


15

(이미 여기에 답을 썼지 만 2 년 후이 질문을 다시 방문하면 완전히 다른 방법으로 답할 수 있다는 것을 깨달았으므로 이전 답을 그대로두고이 답을 추가하겠습니다.)


Java Generics에서 수행 된 프로세스가 "type erasure"라는 이름을 가질 자격이 있는지 여부는 매우 논쟁의 여지가 있습니다. 일반 유형은 지워지지 않고 원시 유형으로 대체되므로 더 나은 선택은 "유형 절단"인 것 같습니다.

일반적으로 이해되는 의미에서 유형 삭제의 전형적인 기능은 런타임이 액세스하는 데이터의 구조에 대해 "블라인드"되도록 만들어 정적 유형 시스템의 경계 내에 머물도록하는 것입니다. 이것은 컴파일러에 모든 권한을 부여하고 정적 유형만을 기반으로 정리를 증명할 수 있습니다. 또한 코드의 자유도를 제한하여 단순한 추론에 더 많은 권한을 부여함으로써 프로그래머에게 도움이됩니다.

Java의 유형 삭제는이를 달성하지 못합니다. 다음 예제와 같이 컴파일러를 마비시킵니다.

void doStuff(List<Integer> collection) { 
}

void doStuff(List<String> collection) // ERROR: a method cannot have 
                   // overloads which only differ in type parameters

(위의 두 선언은 삭제 후 동일한 메서드 서명으로 축소됩니다.)

반대로 런타임은 여전히 ​​객체의 유형과 그에 대한 이유를 검사 할 수 있지만, 진정한 유형에 대한 통찰력은 삭제로 인해 손상되기 때문에 정적 유형 위반은 달성하기 쉽고 방지하기 어렵습니다.

더욱 복잡하게 만들기 위해 원본 및 지워진 유형 서명이 공존하며 컴파일 중에 병렬로 고려됩니다. 이는 전체 프로세스가 런타임에서 유형 정보를 제거하는 것이 아니라 이전 버전과의 호환성을 유지하기 위해 일반 유형 시스템을 레거시 원시 유형 시스템으로 통합하는 것이기 때문입니다. 이 gem은 전형적인 예입니다.

public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)

( extends Object삭제 된 서명의 이전 버전과의 호환성을 유지하려면 중복 을 추가해야했습니다.)

이제이를 염두에두고 인용문을 다시 살펴 보겠습니다.

Java 사용자가 유형 삭제에 대해 불평 할 때 재미 있습니다. 이것이 Java가 옳은 유일한 방법입니다.

Java가 정확히 무엇을 얻었습니까? 의미에 관계없이 단어 자체입니까? 대조적으로 겸손한 int유형을 살펴보십시오 . 런타임 유형 검사가 수행되지 않거나 가능하지 않으며 실행은 항상 완벽하게 유형 안전합니다. 이것이 바로 유형 삭제가 올바르게 수행되었을 때 나타나는 모습입니다. 거기에 있는지조차 알지 못합니다.


10

내가 여기서 전혀 고려하지 않는 한 가지는 OOP의 런타임 다형성 이 근본적으로 런타임에서 유형의 수정에 의존한다는 것입니다. 수정 된 유형에 의해 중추가 제자리에있는 언어가 유형 시스템에 주요 확장을 도입하고 유형 삭제를 기반으로 할 때인지 부조화는 피할 수없는 결과입니다. 이것이 바로 Java 커뮤니티에 일어난 일입니다. 이것이 타입 삭제가 많은 논란을 불러 일으키는 이유이며 궁극적 으로 Java의 향후 릴리스에서이를 취소 할 계획 이있는 이유 입니다. Java 사용자의 불만에서 재미있는 것을 발견하는 것은 Java의 정신에 대한 정직한 오해 또는 의식적으로 비난하는 농담을 배반합니다.

"삭제는 Java가 제대로 된 유일한 것"이라는 주장은 "함수 인수의 런타임 유형에 대한 동적 디스패치에 기반한 모든 언어가 근본적으로 결함이있다"는 주장을 암시합니다. 확실히 그 자체로 합법적 인 주장이고 Java를 포함한 모든 OOP 언어에 대한 유효한 비판으로 간주 될 수있는 주장이지만, 런타임 다형성이있는 Java 컨텍스트 내에서 기능을 평가하고 비판 할 수있는 중심점으로 자리 매김 할 수는 없습니다. 공리입니다.

요약하면, "유형 삭제는 언어 디자인에서가는 길이다"라고 타당하게 말할 수 있지만, Java 내에서 유형 삭제를 지원하는 위치는 단순히 너무 늦었고 이미 너무 늦었 기 때문에 잘못된 위치에 있습니다. Oak가 Sun에 의해 수용되고 Java로 이름이 변경되었을 때.



정적 타이핑 자체가 프로그래밍 언어 설계에서 올바른 방향인지 여부에 관해서는 프로그래밍 활동을 구성한다고 생각하는 훨씬 더 넓은 철학적 맥락에 적합 합니다 . 고전적인 수학 전통에서 분명하게 파생 된 한 학파는 프로그램을 하나의 수학적 개념 또는 기타 (명제, 함수 등)의 사례로 간주하지만 프로그래밍을 방법으로 보는 완전히 다른 종류의 접근 방식이 있습니다. 기계와 대화하고 우리가 원하는 것을 설명하십시오. 이 관점에서 프로그램은 정적으로 형식화 된 프로그램의 세 심하게 세워진 부속물과 극적으로 반대되는 역동적이고 유기적으로 성장하는 실체입니다.

동적 언어를 그 방향의 한 단계로 간주하는 것은 자연스러워 보일 것입니다. 프로그램의 일관성은 하향식 방식으로 강요 하는 선험적 구성 요소 없이 상향식으로 나타납니다 . 이 패러다임은 우리 인간이 개발과 학습을 통해 우리가되는 과정을 모델링하는 단계로 볼 수 있습니다.


감사합니다. 이 질문에 대해 기대했던 관련 철학에 대한 정확한 이해입니다.
Daniel Langdon 2014

동적 디스패치는 수정 된 유형에 전혀 의존하지 않습니다. 종종 프로그래밍 문헌에서 "태그"라고 부르는 것에 의존하지만 그 후에도 태그를 사용할 필요는 없습니다. 인터페이스 (객체 용)를 만들고 해당 인터페이스의 익명 인스턴스를 만드는 경우 수정 된 유형이 전혀 필요하지 않은 동적 디스패치가 수행됩니다.
Sukant Hajra

@sukanthajra 그냥 머리가 갈라지는 것입니다. 태그는 인스턴스가 나타내는 동작의 종류를 해결하는 데 필요한 런타임 정보입니다. 그것은 정적 유형 시스템의 범위를 벗어 났으며 논의중인 개념의 본질입니다.
Marko Topolnik

"합 유형"이라고하는 이러한 추론을 위해 설계된 매우 정적 인 유형이 있습니다. "변형 유형"이라고도합니다 (Benjamin Pierce의 매우 좋은 유형 및 프로그래밍 언어 책에서). 그래서 이것은 머리카락이 전혀 갈라지는 것이 아닙니다. 또한 완전히 완전히 태그가없는 접근 방식을 위해 catamorphism / fold / visitor를 사용할 수 있습니다.
Sukant Hajra 2016-06-18

이론 수업에 감사하지만 관련성이 보이지 않습니다. 우리의 주제는 Java의 유형 시스템입니다.
Marko Topolnik

9

동일한 대화에서 동일한 사용자의 후속 게시물 :

새로운 T는 깨진 프로그램입니다. "모든 명제는 참되다"는 주장과 동형이다. 나는 이것에 크지 않습니다.

(이것은 "어떤 상황에서는 '새로운 T'가 더 좋을 것 같다"는 다른 사용자의 진술에 대한 응답으로, new T()유형 삭제로 인해 불가능하다는 생각 입니다. (이것은 논란의 여지 T가 있습니다. 런타임, 추상 클래스 또는 인터페이스이거나이거나 Void인수가없는 생성자가 부족하거나 인수가없는 생성자가 private 일 수 있습니다 (예 : 싱글 톤 클래스 여야하기 때문에). 인수가없는 생성자는 제네릭 메서드가 포착하거나 지정하지 않는 확인 된 예외를 지정할 수 있습니다.하지만 그게 전제였습니다. 그럼에도 불구하고 삭제없이 최소한 T.class.newInstance()이러한 문제를 처리하는을 작성할 수 있다는 것은 사실입니다 .))

유형이 명제와 동형이라는이 견해는 사용자가 형식 유형 이론에 대한 배경 지식을 가지고 있음을 시사합니다. (S) 그는 "동적 유형"또는 "런타임 유형"을 좋아하지 않을 가능성이 높으며 다운 캐스트 instanceof및 리플렉션 등이 없는 Java를 선호합니다 . (매우 풍부한 (정적) 유형 시스템이 있고 동적 의미가 유형 정보에 전혀 의존하지 않는 표준 ML과 같은 언어를 생각해보십시오.)

그것은 사용자가 조업이다, 그런데 마음에 가치 유지,이다 : (들)은 그 가능성이 진심 (정적)으로 입력 된 언어를 선호하면서 (들)이 자신이되어 있지 진심으로 해당 뷰의 다른 사람을 설득하려고합니다. 오히려 원래 트윗의 주된 목적은 동의하지 않는 사람들을 조롱하는 것이었고, 동의하지 않는 사람들 중 일부가 착각 한 후 사용자는 "자바가 유형 삭제를하는 이유는 Wadler 등이 무엇을 알고 있기 때문입니다. 그들은 자바 사용자와 달리하고 있습니다. " 불행히도, 이것은 그가 실제로 무슨 생각을하고 있는지 알아 내기 어렵게 만듭니다. 하지만 다행히도 그렇게하는 것이 그다지 중요하지 않다는 의미이기도합니다. 자신의 의견을 실제 깊이있는 사람들은 일반적으로 트롤에 의존하지 않는 아주 이 내용없는.


2
유형 삭제가 있기 때문에 Java로 컴파일되지 않습니다
Mark Rotteveel 2014 년

1
@MarkRotteveel : 감사합니다. 나는 그것을 설명하고 논평하기 위해 단락을 추가했습니다.
ruakh

1
upvotes와 downvotes의 혼합으로 볼 때 이것은 내가 게시 한 답변 중 가장 논란이 많은 답변입니다. 이상하게도 자랑 스럽습니다.
ruakh

6

한 가지 좋은 점은 제네릭이 도입되었을 때 JVM을 변경할 필요가 없다는 것입니다. Java는 컴파일러 수준에서만 제네릭을 구현합니다.


1
JVM은 무엇에 비해 변경됩니까? 템플릿에도 JVM 변경이 필요하지 않습니다.
Marquis of Lorne

5

유형 삭제가 좋은 이유는 불가능하게 만드는 것은 해 롭기 때문입니다. 런타임시 유형 인수 검사를 방지하면 프로그램을 더 쉽게 이해하고 추론 할 수 있습니다.

다소 반 직관적 인 관찰은 함수 시그니처가 일반적 일 때 이해하기 쉬워진다는 것입니다. 가능한 구현 수가 줄어들 기 때문입니다. 이 시그니처가있는 방법을 고려해보십시오. 우리는 어떤 식 으로든 부작용이 없다는 것을 알고 있습니다.

public List<Integer> XXX(final List<Integer> l);

이 기능의 가능한 구현은 무엇입니까? 매우 많은. 이 함수가 무엇을하는지 거의 알 수 없습니다. 입력 목록을 뒤집을 수 있습니다. int를 함께 짝을 이루고 합산하여 절반 크기의 목록을 반환 할 수 있습니다. 상상할 수있는 다른 많은 가능성이 있습니다. 이제 다음을 고려하십시오.

public <T> List<T> XXX(final List<T> l);

이 기능의 구현은 몇 개입니까? 구현이 요소의 유형을 알 수 없기 때문에 이제 많은 구현을 제외 할 수 있습니다. 요소를 결합하거나 목록에 추가하거나 필터링 할 수 없습니다. 우리는 다음과 같은 것으로 제한됩니다 : 신원 (목록 변경 없음), 요소 삭제 또는 목록 반전 이 함수는 서명만으로 추론하기가 더 쉽습니다.

예외는… 자바에서는 항상 타입 시스템을 속일 수 있습니다. 이 제네릭 메서드의 구현은 instanceof검사 및 / 또는 임의의 유형으로의 캐스트와 같은 것을 사용할 수 있기 때문에 유형 서명을 기반으로 한 추론은 쉽게 쓸모 없게 될 수 있습니다. 이 기능은 수있는 요소의 유형을 검사하고 결과에 따라 사물의 수를 않습니다. 이러한 런타임 해킹이 허용되면 매개 변수화 된 메서드 서명이 우리에게 훨씬 덜 유용 해집니다.

Java에 유형 삭제가없는 경우 (즉, 유형 인수가 런타임에 수정 됨) 이는 단순히 이런 종류의 추론을 손상시키는 더 많은 허위 행위를 허용 합니다. 위의 예에서 구현은 목록에 요소가 하나 이상있는 경우에만 형식 서명에 의해 설정된 기대치를 위반할 수 있습니다. 그러나 수정 된 경우 T목록이 비어 있어도 그렇게 할 수 있습니다. 수정 된 유형은 코드에 대한 이해를 방해 할 가능성을 (이미 매우 많이) 증가시킬뿐입니다.

유형 삭제는 언어를 덜 "강력하게"만듭니다. 그러나 어떤 형태의 "권력"은 실제로 해 롭습니다.


1
제네릭은 자바 개발자가 "이해하고 추론"하기에는 너무 복잡하거나 기회가 주어지면 스스로를 쏘지 않을 것이라고 믿을 수 없다고 주장하는 것 같습니다. 후자는 자바의 정신과 일치하는 것처럼 보이지만 (서명되지 않은 유형 등은 아님) 개발자에게는 약간의 굴욕감을주는 것 같습니다
Basic

1
@Basic 내가 의미하는 바가 아니기 때문에 나는 내 자신을 매우 나쁘게 표현했을 것입니다. 문제는 제네릭이 복잡하다는 것이 아니라 캐스팅과 같은 것들이 instanceof유형에 따라 코드가 수행하는 작업을 추론하는 능력을 방해 한다는 것 입니다. Java가 유형 인수를 수정하면이 문제를 더욱 악화시킬 수 있습니다. 런타임에 유형을 지우면 유형 시스템을 더 유용하게 만드는 효과가 있습니다.
Lachlan

@lachlan은 나에게 완벽하게 이해가되었고, 일반적인 읽기가 Java 개발자에게 모욕을 줄 것이라고 생각하지 않습니다.
Rob Grant

3

이것은 직접적인 대답이 아닙니다 (OP는 "이점은 무엇입니까"라고 물었고, 저는 "단점은 무엇입니까"라고 대답했습니다))

C # 유형 시스템에 비해 Java 유형 삭제는 두 raeson에게 진정한 고통입니다.

인터페이스를 두 번 구현할 수 없습니다.

C #에서 당신이 모두를 구현할 수 IEnumerable<T1>IEnumerable<T2>두 종류가 공통 조상을 공유하지 않는 특히, 안전하게 (즉, 자신의 조상 이다 Object ).

실용적인 예 : Spring Framework에서는 ApplicationListener<? extends ApplicationEvent>여러 번 구현할 수 없습니다 . T테스트 할 필요에 따라 다른 동작이 필요한 경우instanceof

새로운 T ()를 할 수 없습니다.

(그리고 그렇게하려면 Class에 대한 참조가 필요합니다)

다른 사람들이 언급했듯이, new T()의 인스턴스를 호출 Class<T>하고 생성자에 필요한 매개 변수를 확인 하는 방식으로 만 리플렉션을 통해 와 동등한 작업을 수행 할 수 있습니다 . C #에서는 매개 변수없는 생성자로 제한 하는 경우 new T() 에만 수행 할 수 있습니다 T. 경우 T그 제약 조건을 존중하지 않는하는 컴파일 오류가 발생합니다.

Java에서는 종종 다음과 같은 메소드를 작성해야합니다.

public <T> T create(....params, Class<T> classOfT)
{

    ... whatever you do
    ... you will end up
    T = classOfT.newInstance();


    ... or more advanced reflection
    Constructor<T> parameterizedConstructorThatYouKnowAbout = classOfT.getConstructor(...,...);
}

위 코드의 단점은 다음과 같습니다.

  • Class.newInstance 는 매개 변수가없는 생성자에서만 작동합니다. 사용할 수없는 경우 ReflectiveOperationException런타임에 throw됩니다.
  • 리플 렉 티드 생성자는 위와 같이 컴파일 타임에 문제를 강조하지 않습니다. 리팩터링하면 인수를 교환하면 런타임에만 알 수 있습니다.

내가 C #의 작성자라면 컴파일 타임에 확인하기 쉬운 하나 이상의 생성자 제약 조건을 지정하는 기능을 도입했을 것입니다 (예를 들어 string,stringparams가 있는 생성자를 요구할 수 있음 ). 하지만 마지막은 추측입니다


2

다른 답변 중 어느 것도 고려하지 않은 추가 요점 : 런타임 타이핑을 사용하는 제네릭이 정말로 필요한 경우 다음 과 같이 직접 구현할 수 있습니다 .

public class GenericClass<T>
{
     private Class<T> targetClass;
     public GenericClass(Class<T> targetClass)
     {
          this.targetClass = targetClass;
     }

이 클래스는 Java가 삭제를 사용하지 않는 경우 기본적으로 달성 할 수있는 모든 작업을 수행 할 수 있습니다. 새 Ts ( T사용할 것으로 예상되는 패턴과 일치하는 생성자가 있다고 가정)를 할당 하거나 Ts의 배열을 할당 할 수 있습니다. 특정 개체가 있는지 여부를 런타임에 동적으로 테스트하고 이에 T따라 동작을 변경합니다.

예를 들면 :

     public T newT () { 
         try {
             return targetClass.newInstance(); 
         } catch(/* I forget which exceptions can be thrown here */) { ... }
     }

     private T value;
     /** @throws ClassCastException if object is not a T */
     public void setValueFromObject (Object object) {
         value = targetClass.cast(object);
     }
}

반대표에 대한 이유를 설명 할 사람이 있습니까? 이것은 내가 볼 수있는 한, Java의 유형 삭제 시스템의 가정 된 단점이 실제로 실제 제한이 아닌 이유에 대한 완벽한 설명입니다. 그래서 무엇이 문제입니까?
Jules

나는 찬성하고 있습니다. 내가 런타임 유형 검사를 지원하는 것은 아니지만이 트릭은 소금 광산에서 유용 할 수 있습니다.
techtangents

3
Java 소금 광산의 언어이므로 런타임 유형 정보가 부족하기 때문에 이것이 필요합니다. 바라건대, 타입 삭제는 Java의 차기 버전에서 취소되어 제네릭이 훨씬 더 유용한 기능이 될 것입니다. 현재 합의는 추력 / 무게 비율이 1 미만이라는 것입니다.
Marko Topolnik 2014

글쎄요 ... TargetType이 제네릭 자체를 사용하지 않는 한. 그러나 그것은 꽤 제한적인 것 같습니다.
Basic

일반 유형에서도 작동합니다. 유일한 제약은 인스턴스를 만들려면 올바른 인수 유형을 가진 생성자가 필요하지만 내가 아는 다른 모든 언어에는 동일한 제약 조건이 있으므로 제한이 보이지 않는다는 것입니다.
Jules

0

동일한 코드가 여러 유형에 사용되기 때문에 C ++와 유사한 코드 부풀림을 방지합니다. 그러나 유형 삭제에는 가상 디스패치가 필요하지만 C ++ 코드 블로 트 접근 방식은 비가 상 디스패치 제네릭을 수행 할 수 있습니다.


3
하지만 이러한 가상 디스패치의 대부분은 런타임에 정적 디스패치로 변환되므로보기만큼 나쁘지는 않습니다.
Piotr Kołaczkowski

1
매우 사실입니다. 런타임에 컴파일러를 사용할 수 있기 때문에 Java는 C ++에 비해 많은 멋진 작업을 수행하고 고성능을 달성 할 수 있습니다.
네크로맨서

와우, 내가 어딘가의 아래를 쓸 수 자바 것은 CPP에 고성능 상대 .. 달성
jungle_mole

1
@jungle_mole 네, 감사합니다. 프로파일 링 후 코드를 다시 컴파일하기 위해 런타임에 컴파일러를 사용할 수 있다는 이점을 깨닫는 사람은 거의 없습니다. (또는 가비지 콜렉션은 가비지 콜렉션이 big-oh (garbage)라는 순진하고 잘못된 믿음이 아니라 big-oh (non-garbage)입니다. 배당)). 사람들이 자신의 공동체가 믿는 것을 맹목적으로 받아들이고 첫 번째 원칙에서 추론을 멈추는 슬픈 세상입니다.
necromancer

0

대부분의 답변은 실제 기술 세부 사항보다 프로그래밍 철학에 더 관심이 있습니다.

그리고이 질문은 5 년이 넘었지만 여전히 질문이 남아 있습니다. 기술적 인 관점에서 유형 삭제가 바람직한 이유는 무엇입니까? 결국 대답은 다소 간단합니다 (상위 수준에서) : https://en.wikipedia.org/wiki/Type_erasure

C ++ 템플릿은 런타임에 존재하지 않습니다. 컴파일러는 각 호출에 대해 완전히 최적화 된 버전을 내 보냅니다. 즉, 실행이 유형 정보에 의존하지 않습니다. 그러나 JIT는 동일한 기능의 다른 버전을 어떻게 처리합니까? 하나의 기능 만 갖는 것이 더 낫지 않을까요? JIT가 모든 다른 버전을 최적화하는 것을 원하지 않을 것입니다. 그렇다면 형식 안전성은 어떻습니까? 창문 밖으로 나가야 할 것 같아요.

하지만 잠깐만 요 : .NET은 어떻게할까요? 반사! 이렇게하면 하나의 함수 만 최적화하고 런타임 유형 정보도 얻을 수 있습니다. 이것이 .NET 제네릭이 더 느려 졌던 이유입니다 (훨씬 좋아졌지만). 나는 그것이 편리하지 않다고 주장하는 것이 아닙니다! 그러나 비용이 많이 들고 절대적으로 필요하지 않을 때 사용해서는 안됩니다 (컴파일러 / 인터프리터가 어쨌든 리플렉션에 의존하기 때문에 동적 형식 언어에서는 비용이 많이 드는 것으로 간주되지 않습니다).

이렇게하면 유형 삭제를 사용하는 일반 프로그래밍이 오버 헤드가 거의 없습니다 (일부 런타임 검사 / 캐스트가 여전히 필요함) : https://docs.oracle.com/javase/tutorial/java/generics/erasure.html

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