program tip

Java의 제네릭 구현이 잘못되었다고 주장하는 이유는 무엇입니까?

radiobox 2020. 7. 30. 10:15
반응형

Java의 제네릭 구현이 잘못되었다고 주장하는 이유는 무엇입니까?


제네릭을 사용하면 Java가 제대로 이해하지 못하는 경우가 종종 있습니다. (가장 가까운 참조, 여기 )

내 미경험을 용서해주십시오. 그러나 무엇이 더 좋을까요?


나쁜:

  • 컴파일 할 때 타입 정보가 손실되므로 실행 시간에 "의미"인 타입을 알 수 없습니다
  • 값 유형에 사용할 수 없습니다 (이것은 더 큰 것입니다-.NET에서는 List<byte>실제로 byte[]예를 들어 지원되며 권투가 필요하지 않습니다)
  • IMO (Generic Methods Suck) 호출을위한 구문
  • 제약 조건에 대한 구문이 혼동 될 수 있습니다
  • 와일드 카드는 일반적으로 혼란 스럽다
  • 위의 주조로 인한 다양한 제한-주조 등

좋은:

  • 와일드 카드를 사용하면 호출 측에서 공분산 / 공분산을 지정할 수 있습니다. 이는 많은 상황에서 매우 깔끔합니다.
  • 아무것도 아닌 것보다 낫습니다!

가장 큰 문제는 Java 제네릭이 컴파일 타임 일 뿐이며 런타임시이를 대체 할 수 있다는 것입니다. C #은 런타임 검사가 더 많기 때문에 칭찬받습니다. 이 게시물 에는 정말 좋은 토론 있으며 다른 토론과 연결됩니다.


주요 문제점은 Java가 실제로 런타임에 제네릭을 가지고 있지 않다는 것입니다. 컴파일 타임 기능입니다.

Java로 일반 클래스를 작성할 때 "Type Erasure"라는 메소드를 사용하여 클래스에서 모든 일반 유형을 실제로 제거하고 본질적으로 Object로 대체하십시오. 마일리지가 높은 제네릭 버전은 컴파일러가 메서드 본문에 나타날 때마다 지정된 제네릭 형식에 캐스트를 삽입하기 만하면됩니다.

이것은 많은 단점이 있습니다. 가장 큰 IMHO 중 하나는 리플렉션을 사용하여 일반 유형을 검사 할 수 없다는 것입니다. 바이트 코드에서 유형은 실제로 제네릭이 아니므로 제네릭으로 검사 할 수 없습니다.

차이점에 대한 훌륭한 개요 : http://www.jprl.com/Blog/archive/development/2007/Aug-31.html


  1. 런타임 구현 (즉, 유형 삭제가 아님)
  2. 기본 유형을 사용하는 기능 ((1)과 관련됨)
  3. 와일드 카드는 구문에 유용하지만 언제 사용해야하는지 아는 것이 많은 사람들을 방해합니다.
  4. 성능 향상이 없습니다 ((1) 때문에 Java 제네릭은 객체를 캐스팅하기위한 구문 설탕입니다).

(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 제네릭이 더 좋다고 생각합니다.


논란의 여지가있는 의견을 버리겠습니다. 제네릭은 언어를 복잡하게하고 코드를 복잡하게합니다. 예를 들어 문자열을 문자열 목록에 매핑하는 맵이 있다고 가정 해 봅시다. 옛날에는 이것을 간단히

Map someMap;

이제는 다음과 같이 선언해야합니다.

Map<String, List<String>> someMap;

그리고 나는 그것을 어떤 방법으로 전달할 때마다 그 큰 긴 선언을 다시 반복해야합니다. 내 의견으로는, 모든 여분의 타이핑은 개발자를 혼란스럽게하고 그를 "영역"에서 빠져 나옵니다. 또한 코드에 많은 크래프트가 가득 차면 나중에 다시 돌아와서 모든 크래프트를 빠르게 탐색하여 중요한 논리를 찾기가 어렵습니다.

자바는 이미 가장 많이 사용되는 언어 중 하나로 널리 알려져 있으며 제네릭은 그 문제를 추가합니다.

그리고 당신은 그 모든 추가 정보를 위해 무엇을 실제로 사나요? 누군가가 문자열을 보유 해야하는 컬렉션에 정수를 넣거나 누군가가 정수 컬렉션에서 문자열을 가져 오려고 할 때 실제로 문제가 발생한 적이 있습니까? 상용 Java 응용 프로그램을 구축하는 데있어 10 년 간의 경험에서이 문제는 결코 큰 오류의 원인이되지 않았습니다. 그래서, 당신이 여분의 세부 정보를 얻기 위해 무엇을 얻고 있는지 잘 모르겠습니다. 정말 관료 수하물로 생각 나네요.

이제 논란의 여지가 있습니다. Java 1.4에서 컬렉션의 가장 큰 문제로 보는 것은 어디에서나 타입 캐스트해야한다는 것입니다. 나는 그 타입 캐스트를 제네릭과 같은 많은 문제가있는 추가의 장황한 횡목으로 본다. 예를 들어, 나는 단지 할 수 없습니다

List someList = someMap.get("some key");

해야 돼

List someList = (List) someMap.get("some key");

The reason, of course, is that get() returns an Object which is a supertype of List. So the assignment can't be made without a typecast. Again, think about how much that rule really buys you. From my experience, not much.

I think Java would have been way better off if 1) it had not added generics but 2) instead had allowed implicit casting from a supertype to a subtype. Let incorrect casts be caught at runtime. Then I could have had the simplicity of defining

Map someMap;

and later doing

List someList = someMap.get("some key");

all the cruft would be gone, and I really don't think I'd be introducing a big new source of bugs into my code.


Another side effect of them being compile-time and not run time is that you can't call the constructor of the generic type. So you can't use them to implement a generic factory...


   public class MyClass {
     public T getStuff() {
       return new T();
     }
    }

--jeffk++


Java generics are checked for correctness at compile time and then all type information is removed (the process is called type erasure. Thus, generic List<Integer> will be reduced to its raw type, non-generic List, which can contain objects of arbitrary class.

This results in being able to insert arbitrary objects to the list at runtime, as well as it's now impossible to tell what types were used as generic parameters. The latter in turn results in

ArrayList<Integer> li = new ArrayList<Integer>();
ArrayList<Float> lf = new ArrayList<Float>();
if(li.getClass() == lf.getClass()) // evaluates to true
  System.out.println("Equal");

Ignoring the whole type erasure mess, generics as specified just don't work.

This compiles:

List<Integer> x = Collections.emptyList();

But this is a syntax error:

foo(Collections.emptyList());

Where foo is defined as:

void foo(List<Integer> x) { /* method body not important */ }

So whether an expression type checks depends on whether it is being assigned to a local variable or an actual parameter of a method call. How crazy is that?


The introduction of generics into Java was a difficult task because the architects were trying to balance functionality, ease of use, and backward compatibility with legacy code. Quite expectedly, compromises had to be made.

There are some who also feel that Java's implementation of generics increased the complexity of the language to an unacceptable level (see Ken Arnold's "Generics Considered Harmful"). Angelika Langer's Generics FAQs gives a pretty good idea as to how complicated things can become.


I wish this was a wiki so I could add to other people... but...

Problems:

  • Type Erasure (no runtime availability)
  • No support for primative types
  • Incompatability with Annotations (they were both added in 1.5 I'm still not sure why annotations don't allow generics aside from rushing the features)
  • Incompatability with Arrays. (Sometimes I really want to do somthing like Class<? extends MyObject>[], but I'm not allowed)
  • Wierd wildcard syntax and behavior
  • The fact that generic support is inconsistant across Java classes. They added it to most of the collections methods, but every once in a while, you run into an instance where its not there.

Java doesn't enforce Generics at run time, only at compile time.

This means that you can do interesting things like adding the wrong types to generic Collections.


Java generics are compile-time only and are compiled into non-generic code. In C#, the actual compiled MSIL is generic. This has huge implications for performance because Java still casts during runtime. See here for more.


If you listen to Java Posse #279 - Interview with Joe Darcy and Alex Buckley, they talk about this issue. That also links to a Neal Gafter blog post titled Reified Generics for Java that says:

Many people are unsatisfied with the restrictions caused by the way generics are implemented in Java. Specifically, they are unhappy that generic type parameters are not reified: they are not available at runtime. Generics are implemented using erasure, in which generic type parameters are simply removed at runtime.

That blog post, references an older entry, Puzzling Through Erasure: answer section, that stressed the point about migration compatibility in the requirements.

The goal was to provide backwards compatibility of both source and object code, and also migration compatibility.

참고URL : https://stackoverflow.com/questions/520527/why-do-some-claim-that-javas-implementation-of-generics-is-bad

반응형