답변:
나쁜:
List<byte>실제로 byte[]예를 들어 지원되며 권투가 필요하지 않습니다)좋은:
가장 큰 문제는 Java 제네릭이 컴파일 타임에만 해당되며 런타임에이를 전복 할 수 있다는 것입니다. C #은 더 많은 런타임 검사를 수행하기 때문에 칭찬을받습니다. 이 게시물 에는 정말 좋은 토론 이 있으며 다른 토론으로 연결됩니다.
Class객체를 전달할 수 있습니다 .
주요 문제는 Java가 실제로 런타임에 제네릭을 가지고 있지 않다는 것입니다. 컴파일 타임 기능입니다.
Java에서 제네릭 클래스를 만들 때 "Type Erasure"라는 메서드를 사용하여 실제로 클래스에서 모든 제네릭 유형을 제거하고 기본적으로 Object로 대체합니다. 제네릭의 마일 높은 버전은 컴파일러가 메서드 본문에 나타날 때마다 지정된 제네릭 유형에 캐스트를 삽입하는 것입니다.
이것은 많은 단점이 있습니다. 가장 큰 IMHO 중 하나는 리플렉션을 사용하여 제네릭 유형을 검사 할 수 없다는 것입니다. 유형은 실제로 바이트 코드에서 제네릭이 아니므로 제네릭으로 검사 할 수 없습니다.
차이점에 대한 개요 : http://www.jprl.com/Blog/archive/development/2007/Aug-31.html
(1) 매우 이상한 행동을합니다. 제가 생각할 수있는 가장 좋은 예는 다음과 같습니다. 취하다:
public class MyClass<T> {
T getStuff() { ... }
List<String> getOtherStuff() { ... }
}
그런 다음 두 개의 변수를 선언하십시오.
MyClass<T> m1 = ...
MyClass m2 = ...
이제 전화하십시오 getOtherStuff():
List<String> list1 = m1.getOtherStuff();
List<String> list2 = m2.getOtherStuff();
그것이이없는 경우에도 원시 유형 (파라미터 화 된 형태를 의미가 제공되지 않음) 때문에 두 번째는 컴파일러에 의해 벗겨 자사의 제네릭 형식 인수를 가지고 아무것도 파라미터 화 된 형태로 할 수있다.
또한 JDK에서 가장 좋아하는 선언을 언급하겠습니다.
public class Enum<T extends Enum<T>>
와일드 카드 (혼합 된 백)를 제외하고는 .Net 제네릭이 더 낫다고 생각합니다.
public class Redundancy<R extends Redundancy<R>>;)
The expression of type List needs unchecked conversion to conform to List<String>
Enum<T extends Enum<T>>처음에는 이상하거나 중복되어 보일 수 있지만, 적어도 Java / 그것의 제네릭의 제약 내에서 실제로는 꽤 흥미 롭습니다. 열거 형에는 열거 형이 아니라 열거 형으로 values()형식화 된 요소의 배열을 제공하는 정적 메서드가 Enum있으며 해당 형식은 원하는을 의미하는 제네릭 매개 변수에 의해 결정됩니다 Enum<T>. 물론 그 타이핑은 열거 형의 컨텍스트에서만 의미가 있으며 모든 열거 형은의 하위 클래스 Enum이므로 Enum<T extends Enum>. 그러나 Java는 Enum<T extends Enum<T>>일관성 을 위해 원시 유형을 제네릭과 혼합하는 것을 좋아하지 않습니다 .
정말 논란의 여지가있는 의견을 버리려고합니다. 제네릭은 언어를 복잡하게하고 코드를 복잡하게 만듭니다. 예를 들어, 문자열을 문자열 목록에 매핑하는 맵이 있다고 가정 해 보겠습니다. 예전에는 이것을 간단히 다음과 같이 선언 할 수있었습니다.
Map someMap;
이제 다음과 같이 선언해야합니다.
Map<String, List<String>> someMap;
그리고 내가 그것을 어떤 방법으로 전달할 때마다 나는 그 크고 긴 선언을 다시 반복해야한다. 제 생각에, 그 모든 추가 타이핑은 개발자의주의를 산만하게하고 그를 "영역"밖으로 데려갑니다. 또한 코드가 엉망진창으로 가득 차면 나중에 다시 돌아와서 중요한 논리를 찾기 위해 모든 엉뚱한 부분을 빠르게 살펴보기가 어려울 때가 있습니다.
Java는 이미 일반적으로 사용되는 가장 장황한 언어 중 하나라는 평판이 좋지 않으며 제네릭은 그 문제를 추가합니다.
그리고 그 모든 추가 장황함에 대해 실제로 무엇을 구입합니까? 누군가가 Strings를 보유해야하는 컬렉션에 Integer를 넣거나 누군가 Integer 컬렉션에서 String을 꺼내려고하는 곳에서 실제로 몇 번이나 문제를 겪었습니까? 상용 Java 응용 프로그램을 빌드하는 데 10 년 동안 일한 경험을 통해 이것이 큰 오류 원인이 된 적이 없습니다. 그래서 나는 당신이 여분의 장황함을 위해 무엇을 얻고 있는지 정말로 잘 모르겠습니다. 그것은 정말 관료적 인 짐으로 나를 때립니다.
이제 저는 정말 논란이 될 것입니다. Java 1.4에서 컬렉션의 가장 큰 문제는 어디에서나 타입 캐스트가 필요하다는 것입니다. 나는 이러한 타입 캐스트를 제네릭과 동일한 문제가 많이있는 추가적이고 장황한 것으로 봅니다. 예를 들어, 저는
List someList = someMap.get("some key");
나는해야한다
List someList = (List) someMap.get("some key");
물론 그 이유는 get ()이 List의 상위 유형 인 Object를 반환하기 때문입니다. 따라서 typecast 없이는 할당 할 수 없습니다. 다시 말하지만, 그 규칙이 얼마나 당신을 사는지 생각해보십시오. 내 경험으로는 그리 많지 않습니다.
나는 1) 제네릭을 추가하지 않았지만 2) 대신 수퍼 유형에서 하위 유형으로의 암시 적 캐스팅을 허용했다면 Java가 더 나았을 것이라고 생각합니다. 런타임에 잘못된 캐스트를 잡으십시오. 그러면 간단하게 정의 할 수있었습니다.
Map someMap;
그리고 나중에
List someList = someMap.get("some key");
모든 엉터리가 사라질 것이고, 나는 정말로 큰 버그 소스를 내 코드에 도입 할 것이라고 생각하지 않습니다.
런타임이 아닌 컴파일 타임의 또 다른 부작용은 제네릭 형식의 생성자를 호출 할 수 없다는 것입니다. 따라서 일반 공장을 구현하는 데 사용할 수 없습니다.
public class MyClass {
public T getStuff() {
return new T();
}
}
--jeffk ++
Java 제네릭은 컴파일 시간에 정확성을 검사 한 다음 모든 유형 정보가 제거됩니다 (프로세스를 유형 삭제 라고 합니다 . 따라서 제네릭 List<Integer>은 임의 클래스의 객체를 포함 할 수있는 원시 유형 , 비 제네릭 으로 축소됩니다 List.
이로 인해 런타임에 목록에 임의의 개체를 삽입 할 수있을뿐만 아니라 이제 어떤 유형이 일반 매개 변수로 사용되었는지 알 수 없습니다. 후자는 차례로 결과
ArrayList<Integer> li = new ArrayList<Integer>();
ArrayList<Float> lf = new ArrayList<Float>();
if(li.getClass() == lf.getClass()) // evaluates to true
System.out.println("Equal");
다른 사람에게 추가 할 수 있도록 위키 였으면 좋겠는데 ...하지만 ...
문제점 :
<습니까? MyObject >[]를 확장 하지만 허용되지 않습니다)전체 유형 지우기 혼란을 무시하면 지정된 제네릭이 작동하지 않습니다.
이것은 다음을 컴파일합니다.
List<Integer> x = Collections.emptyList();
그러나 이것은 구문 오류입니다.
foo(Collections.emptyList());
여기서 foo는 다음과 같이 정의됩니다.
void foo(List<Integer> x) { /* method body not important */ }
따라서 표현식 유형이 검사되는지 여부는 로컬 변수에 할당되는지 아니면 메서드 호출의 실제 매개 변수에 할당되는지에 따라 다릅니다. 얼마나 미쳤어?
Java에 제네릭을 도입하는 것은 어려운 작업이었습니다. 설계자가 기능, 사용 편의성 및 레거시 코드와의 하위 호환성 간의 균형을 맞추려고했기 때문입니다. 예상대로 타협을해야했습니다.
Java의 제네릭 구현이 언어의 복잡성을 허용 할 수없는 수준으로 증가 시켰다고 생각하는 사람들도 있습니다 (Ken Arnold의 " Generics Thoughmful "참조). Angelika Langer의 Generics FAQs 는 상황이 얼마나 복잡해질 수 있는지에 대한 좋은 아이디어를 제공합니다.
Java는 런타임에 Generics를 적용하지 않고 컴파일 시간에만 적용합니다.
이것은 제네릭 컬렉션에 잘못된 유형을 추가하는 것과 같은 흥미로운 일을 할 수 있음을 의미합니다.
Java 제네릭은 컴파일 타임 전용이며 비 제네릭 코드로 컴파일됩니다. C #에서 실제 컴파일 된 MSIL은 제네릭입니다. Java는 런타임 중에 캐스트하기 때문에 성능에 큰 영향을 미칩니다. 자세한 내용은 여기를 참조하십시오 .
Java Posse # 279-Interview with Joe Darcy 및 Alex Buckley 를 들으면 이 문제에 대해 이야기합니다. 또한 Reified Generics for Java 라는 제목의 Neal Gafter 블로그 게시물로 연결됩니다 .
많은 사람들이 제네릭이 Java에서 구현되는 방식으로 인해 발생하는 제한에 만족하지 않습니다. 특히, 제네릭 유형 매개 변수가 수정되지 않은 것에 불만이 있습니다. 런타임에 사용할 수 없습니다. 제네릭은 삭제를 사용하여 구현되며, 제네릭 유형 매개 변수는 런타임에 간단히 제거됩니다.
이 블로그 게시물 은 요구 사항에서 마이그레이션 호환성 에 대한 요점을 강조한 이전 항목 인 Puzzling Through Erasure : 답변 섹션을 참조 합니다.
목표는 소스 및 개체 코드의 하위 호환성과 마이그레이션 호환성을 제공하는 것이 었습니다.