it-swarm-ko.tech

제네릭에 대해 멋진 점은 무엇입니까?

이 소프트볼을 공원 밖으로 나가고 싶은 사람에게 제공하겠다고 생각했습니다. 제네릭이란 무엇이며 제네릭의 장점은 무엇이며, 왜, 어디서, 어떻게 사용해야합니까? 기본적으로 유지하십시오. 감사.

80
MrBoJangles
  • Type-safe, 즉 List <string>이 ​​문자열 목록임을 보장하는 코드/사용 라이브러리 메서드를 작성할 수 있습니다.
  • 제네릭을 사용하면 컴파일러에서 형식 안전성을 위해 코드에서 컴파일 타임 검사를 수행 할 수 있습니다. 즉, 해당 문자열 목록에 int를 넣으려고합니까? ArrayList를 사용하면 런타임 투명성이 떨어집니다.
  • .net이 값 유형을 참조 유형으로 변환하거나 그 반대로 변환해야 함 ) 또는 상자에서 필요한 참조 유형으로 캐스트해야하므로 객체를 사용하는 것보다 빠릅니다.
  • 기본 동작이 동일한 여러 유형에 적용 가능한 코드를 작성할 수 있습니다. 즉, Dictionary <string, int>는 Dictionary <DateTime, double>과 동일한 기본 코드를 사용합니다. 제네릭을 사용하여 프레임 워크 팀은 앞서 언급 한 장점으로 두 결과를 모두 달성하기 위해 하나의 코드 만 작성하면됩니다.
120
ljs

나는 정말로 자신을 반복하는 것을 싫어한다. 나는 필요한 것보다 더 자주 같은 것을 타이핑하는 것을 싫어한다. 나는 약간의 차이로 물건을 여러 번 쉬는 것을 좋아하지 않습니다.

작성하는 대신 :

class MyObjectList  {
   MyObject get(int index) {...}
}
class MyOtherObjectList  {
   MyOtherObject get(int index) {...}
}
class AnotherObjectList  {
   AnotherObject get(int index) {...}
}

재사용 가능한 클래스 하나를 만들 수 있습니다 ... (어떤 이유로 원시 컬렉션을 사용하지 않으려는 경우)

class MyList<T> {
   T get(int index) { ... }
}

나는 지금 3 배 더 효율적이며 사본 하나만 유지하면됩니다. 왜 적은 코드를 유지하고 싶지 않습니까?

다른 클래스와 상호 작용해야하는 Callable<T> 또는 Reference<T>와 같은 비 수집 클래스에도 적용됩니다. Callable<T>Future<T> 및 기타 모든 관련 클래스를 확장하여 형식이 안전한 버전을 만드시겠습니까?

난 아니야.

46
James Schek

컴파일 타임에 타입 검사를 수행하기 때문에 typecast를하지 않아도됩니다 Java generics의 가장 큰 장점 중 하나입니다. 이렇게하면 ClassCastExceptions는 런타임에 발생할 수 있으며보다 강력한 코드로 이어질 수 있습니다.

그러나 나는 당신이 그것을 완전히 알고 있다고 생각합니다.

제네릭을 볼 때마다 두통이 생깁니다. Java의 가장 중요한 부분은 단순하고 최소한의 구문이며 제네릭은 단순하지 않으며 상당한 양의 새로운 구문을 추가하는 것입니다.

처음에는 제네릭의 이점도 보지 못했습니다. 1.4 구문에서 Java를 배우기 시작했지만 (Java 5는 당시에 나왔지만)) 제네릭을 만났을 때 더 많은 코드라고 생각했습니다. 글을 쓰고 실제로 이점을 이해하지 못했습니다.

현대 IDE를 사용하면 제네릭으로 코드를 쉽게 작성할 수 있습니다.

가장 현대적이고 괜찮은 IDE는 특히 코드 완성과 함께 제네릭으로 코드를 작성하는 데 도움이 될만큼 똑똑합니다.

다음은 HashMapMap<String, Integer>를 만드는 예입니다. 입력해야 할 코드는 다음과 같습니다.

Map<String, Integer> m = new HashMap<String, Integer>();

그리고 실제로, 그것은 새로운 HashMap를 만들기 위해 타이핑하는 것이 많습니다. 그러나 실제로는 Eclipse가 내가 필요한 것을 알기 전에 이것을 많이 입력해야했습니다.

Map<String, Integer> m = new Ha Ctrl+Space

사실, 후보 목록에서 HashMap를 선택해야했지만 기본적으로 IDE는 제네릭 형식을 포함하여 추가 할 항목을 알고있었습니다. 나쁘지 않아.

또한 형식이 알려져 있기 때문에 일반 컬렉션에서 요소를 검색 할 때 IDE는 해당 개체가 이미 선언 된 형식의 개체 인 것처럼 동작하므로 캐스팅 할 필요가 없습니다. IDE에 대해 객체 유형이 무엇인지 알 수 있습니다.

제네릭의 주요 장점은 새로운 Java 5 기능과 잘 작동하는 방식에서 비롯됩니다. 다음은 정수를 Set에 던지는 예입니다. 총계 계산 :

Set<Integer> set = new HashSet<Integer>();
set.add(10);
set.add(42);

int total = 0;
for (int i : set) {
  total += i;
}

이 코드에는 세 가지 새로운 Java 5 개의 기능이 있습니다.

먼저, 기본 요소의 제네릭 및 오토 박싱은 다음 줄을 허용합니다.

set.add(10);
set.add(42);

정수 10는 값이 10Integer로 자동 박스됩니다. (42와 동일). 그런 다음 IntegerSets를 보유하는 것으로 알려진 Integer에 던져집니다. String를 던지려고하면 컴파일 오류가 발생합니다.

다음으로 for-each 루프는 다음 세 가지를 모두 수행합니다.

for (int i : set) {
  total += i;
}

먼저, Sets를 포함하는 Integer는 for-each 루프에서 사용됩니다. 각 요소는 int로 선언되며 Integer이 기본 int으로 다시 언 박싱 될 때 허용됩니다. 그리고이 unboxing이 발생한다는 사실은 generics가 IntegerSets가 있음을 지정하는 데 사용 되었기 때문에 알려져 있습니다.

제네릭은 Java 5)에 도입 된 새로운 기능을 하나로 모으는 접착제가 될 수 있으며 코딩을보다 간단하고 안전하게 만들뿐 아니라 IDE는 대부분 현명한 제안에 도움이 될만큼 똑똑합니다. 일반적으로 더 많은 타이핑이 필요하지 않습니다.

솔직히 Set 예제에서 볼 수 있듯이 Java 5 기능을 사용하면 코드가 더 간결하고 강력 해집니다.

편집-제네릭없는 예제

다음은 제네릭을 사용하지 않는 위의 Set 예를 보여줍니다. 가능하지만 정확히 유쾌하지는 않습니다.

Set set = new HashSet();
set.add(10);
set.add(42);

int total = 0;
for (Object o : set) {
  total += (Integer)o;
}

(참고 : 위의 코드는 컴파일 타임에 확인되지 않은 변환 경고를 생성합니다.)

제네릭이 아닌 컬렉션을 사용하는 경우 컬렉션에 입력 된 형식은 Object 형식의 개체입니다. 따라서이 예에서 Object는 세트에 added되는 것입니다.

set.add(10);
set.add(42);

위의 라인에서 오토 박싱이 실행 중입니다. 프리미티브 int value 1042Integer 객체로 자동 박싱되고 있습니다. Set. 그러나 컴파일러가 Integer가 어떤 유형을 기대해야하는지 알 수 있도록 유형 정보가 없으므로 Object 객체는 Sets로 처리됩니다.

for (Object o : set) {

이것이 중요한 부분입니다. for-each 루프가 작동하는 이유는 SetIterable 인터페이스를 구현하여 유형 정보가있는 Iterator를 반환하기 때문입니다. (Iterator<T>, 즉)

그러나 타입 정보가 없기 때문에 SetIterator의 값을 Sets로 반환하는 Object를 반환합니다. for-each 루프에서 검색되는 요소 must 유형은 Object입니다.

ObjectSet에서 검색 했으므로 추가를 수행하려면 Integer로 수동으로 캐스트해야합니다.

  total += (Integer)o;

여기서 타입 캐스트는 Object에서 Integer로 수행됩니다. 이 경우 우리는 이것이 항상 작동한다는 것을 알고 있지만 수동 타입 캐스팅은 항상 다른 곳에서 약간의 변경이 있으면 손상 될 수있는 깨지기 쉬운 코드라고 생각합니다. (나는 모든 타입 캐스트가 일어나기를 기다리는 ClassCastException라고 생각하지만, 나는 ...)

Integer은 이제 int로 개봉되어 int 변수 total에 추가를 수행 할 수 있습니다.

Java 5의 새로운 기능은 제네릭이 아닌 코드와 함께 사용할 수 있지만 제네릭으로 코드를 작성하는 것만 큼 깨끗하고 간단하지는 않다는 것을 설명 할 수 있기를 바랍니다. 내 의견으로는 Java 5)의 새로운 기능을 최대한 활용하려면 적어도 제네릭을 살펴 봐야합니다. 런타임에 예외를 던집니다.

20
coobird

1.5가 릴리스되기 직전에 Java 버그 데이터베이스를 검색하면 NullPointerException보다 ClassCastException의 버그가 7 배 더 많습니다. 버그 나 최소한 연기 테스트 후에도 지속되는 버그를 찾는 것이 훌륭한 기능인 것 같지는 않습니다.

저에게 제네릭의 큰 장점은 코드 중요한 유형 정보를 문서화한다는 것입니다. 해당 형식 정보를 코드로 문서화하지 않으려면 동적 형식 언어 또는 적어도 암시 적 형식 유추가있는 언어를 사용합니다.

객체의 컬렉션을 자체적으로 유지하는 것은 나쁜 스타일이 아닙니다 (그러나 일반적인 스타일은 캡슐화를 효과적으로 무시하는 것입니다). 오히려 당신이하고있는 것에 달려 있습니다. 컬렉션을 "알고리즘"으로 전달하는 것은 제네릭을 사용하여 (컴파일 타임 또는 컴파일 전) 확인하기가 약간 더 쉽습니다.

15

Java ease parametric polymorphism 의 generics) 형식 매개 변수를 사용하면 형식에 인수를 전달할 수 있습니다. String foo(String s)과 같은 메서드는 특정 문자열뿐만 아니라 모든 문자열 s에 대한 동작이므로 List<T>는 특정 유형뿐만 아니라 모든 유형에 대해 일부 동작을 모델링합니다. List<T>모든 유형 T의 경우 List의 요소가 T입니다라고 말합니다. 따라서 List은 실제로 type constructor입니다. 유형을 인수로 사용하여 결과로 다른 유형을 구성합니다.

다음은 매일 사용하는 일반 유형의 몇 가지 예입니다. 첫째, 매우 유용한 일반 인터페이스 :

public interface F<A, B> {
  public B f(A a);
}

이 인터페이스는 AB의 두 가지 유형에 대해 f을 사용하여 반환하는 함수 (A)가 있다고 말합니다. a B.이 인터페이스를 구현할 때 A 함수를 제공하는 한 Bf는 전자를 가지고 후자를 반환합니다. 다음은 인터페이스 구현 예입니다.

F<Integer, String> intToString = new F<Integer, String>() {
  public String f(int i) {
    return String.valueOf(i);
  }
}

제네릭 이전에는 extends 키워드를 사용하여 subclassing에 의해 다형성을 달성했습니다. 제네릭을 사용하면 실제로 서브 클래 싱을 제거하고 대신 파라 메트릭 다형성을 사용할 수 있습니다. 예를 들어, 모든 유형의 해시 코드를 계산하는 데 사용되는 매개 변수화 된 (일반) 클래스를 고려하십시오. Object.hashCode ()를 재정의하는 대신 다음과 같은 일반 클래스를 사용합니다.

public final class Hash<A> {
  private final F<A, Integer> hashFunction;

  public Hash(final F<A, Integer> f) {
    this.hashFunction = f;
  }

  public int hash(A a) {
    return hashFunction.f(a);
  }
}

이는 상속 계층을 사용하는 것보다 훨씬 유연합니다. 취성 계층을 잠그지 않고 구성 및 매개 변수 다형성을 사용하는 주제를 유지할 수 있기 때문입니다.

Java의 제네릭은 완벽하지 않습니다. 타입에 대해서는 추상화 할 수 있지만, 예를 들어 타입 생성자는 추상화 할 수 없습니다. 즉, "모든 유형 T에 대해"라고 말할 수 있지만 "유형 매개 변수 A를 취하는 모든 유형 T에 대해"라고 말할 수는 없습니다.

이 Java 제네릭, 여기)의 한계에 관한 기사를 썼습니다.

제네릭의 큰 장점은 서브 클래 싱을 피할 수 있다는 것입니다. 서브 클래 싱은 확장하기 어려운 클래스 계층 구조와 전체 계층 구조를 보지 않고 개별적으로 이해하기 어려운 클래스를 만드는 경향이 있습니다.

제네릭 이전에는 Widget, FooWidgetBarWidget로 확장 된 BazWidget과 같은 클래스가있을 수 있으며 제네릭을 사용하면 단일 제네릭 클래스 Widget<A> 생성자에서 Foo, Bar 또는 Baz을 사용하여 Widget<Foo>, Widget<Bar>Widget<Baz>.

10
Apocalisp

제네릭은 권투 및 언 박싱의 성능 저하를 피합니다. 기본적으로 ArrayList와 List <T>를보십시오. 둘 다 같은 핵심 작업을 수행하지만 List <T>는 객체를 상자에서 /받을 필요가 없으므로 훨씬 빠릅니다.

8
Darren Kopp
  • 유형이 지정된 컬렉션-컬렉션을 사용하지 않더라도 다른 라이브러리, 다른 소스에서 처리해야 할 수 있습니다.

  • 클래스 생성시 일반 타이핑 :

    공개 클래스 Foo <T> {공개 T get () ...

  • 캐스팅 방지-나는 항상 같은 것을 싫어했습니다.

    새 비교기 {public int compareTo (Object o) {if (o instanceof classIcareAbout) ...

인터페이스가 객체로 표현되기 때문에 존재 해야하는 조건을 본질적으로 확인하는 경우.

제네릭에 대한 나의 초기 반응은 "너무 지저분하고 너무 복잡하다"와 비슷했습니다. 내 경험은 약간 사용하면 익숙해지며 코드가 없으면 코드가 덜 명확하고 편안하지 않다는 것입니다. 그 외에도, 나머지 Java 세계는 그것들을 사용하므로 결국 프로그램을 사용해야 할 것입니다. 맞습니까?

5
Steve B.

좋은 예를 들자. Foo라는 클래스가 있다고 상상해보십시오.

public class Foo
{
   public string Bar() { return "Bar"; }
}

예 1 이제 Foo 객체 컬렉션을 원합니다. LIst 또는 ArrayList의 두 가지 옵션이 있으며 둘 다 비슷한 방식으로 작동합니다.

Arraylist al = new ArrayList();
List<Foo> fl = new List<Foo>();

//code to add Foos
al.Add(new Foo());
f1.Add(new Foo());

위의 코드에서 Foo 대신 FireTruck 클래스를 추가하려고하면 ArrayList가 추가하지만 Foo의 Generic List는 예외를 발생시킵니다.

예 2

이제 두 배열 목록이 있고 각각에 Bar () 함수를 호출하려고합니다. ArrayList는 Objects로 채워져 있으므로 bar를 호출하기 전에 캐스팅해야합니다. 그러나 Foo의 Generic List에는 Foos 만 포함될 수 있으므로 Bar ()를 직접 호출 할 수 있습니다.

foreach(object o in al)
{
    Foo f = (Foo)o;
    f.Bar();
}

foreach(Foo f in fl)
{
   f.Bar();
}
5
Peter Lange

나는 그들이 당신에게 커스텀 타입을 정의하는 빠른 방법을 제공하기 때문에 그것들을 좋아합니다 (어쨌든 그것들을 사용함에 따라).

예를 들어 문자열과 정수로 구성된 구조를 정의한 다음 해당 구조의 배열에 액세스하는 방법에 대한 전체 객체 및 메소드 세트를 구현 해야하는 대신 사전을 만들 수 있습니다.

Dictionary<int, string> dictionary = new Dictionary<int, string>();

그리고 컴파일러/IDE는 나머지 무거운 작업을 수행합니다. 특히 사전을 사용하면 첫 번째 유형을 키로 사용할 수 있습니다 (반복 된 값 없음).

5
Tom Kidd

메소드/클래스의 핵심 개념이 매개 변수/인스턴스 변수의 특정 데이터 유형 (생각 된 목록, 최대/최소 함수, 이진 검색)에 밀접하게 바인딩되지 않은 메소드 (또는 클래스)를 작성하지 않은 적이 있습니까? 등).

붙여 넣기 재사용이나 강력한 타이핑을 손상시키지 않고 알고리즘/코드를 재사용 할 수 있기를 원하지 않습니까 (예 : List가 아닌 List 것들 hope 은 문자열입니다!)?

그래서 제네릭을 사용하려면 want 해야합니다.

4
Bert F

제네릭은 클래스에서만 사용되는 것이 아니라 메서드에서도 사용될 수 있습니다. 예를 들어 다음 코드 조각을 보자.

private <T extends Throwable> T logAndReturn(T t) {
    logThrowable(t); // some logging method that takes a Throwable
    return t;
}

간단하지만 매우 우아하게 사용할 수 있습니다. 좋은 점은 메소드가 주어진 것을 반환한다는 것입니다. 이는 호출자에게 다시 던져 져야하는 예외를 처리 할 때 도움이됩니다.

    ...
} catch (MyException e) {
    throw logAndReturn(e);
}

요점은 메소드를 통해 유형을 잃어 버리지 않는다는 것입니다. Throwable 대신 올바른 유형의 예외를 처리 할 수 ​​있습니다. 이는 제네릭없이 수행 할 수있는 전부입니다.

이것은 일반적인 방법으로 사용하는 간단한 예입니다. 일반적인 방법으로 할 수있는 몇 가지 다른 깔끔한 작업이 있습니다. 내 생각에 가장 멋진 것은 제네릭으로 유추하는 유형입니다. Josh Bloch의 Effective Java 2 판)에서 가져온 다음 예제를 보자.

...
Map<String, Integer> myMap = createHashMap();
...
public <K, V> Map<K, V> createHashMap() {
    return new HashMap<K, V>();
}

이것은 많이하지 않지만 일반 유형이 길거나 중첩 될 때 혼란을 줄입니다 (예 : Map<String, List<String>>).

3
jigawot

"왜 제네릭을 사용해야합니까?"에 대한 응답으로 Sun Java 문서)에서 :

"Generics는 콜렉션의 유형을 컴파일러와 통신하여이를 확인할 수있는 방법을 제공합니다. 컴파일러가 콜렉션의 요소 유형을 알고 나면 컴파일러는 콜렉션을 일관되게 사용하고 삽입 할 수 있는지 확인할 수 있습니다. 컬렉션에서 꺼내지는 값에 대한 올바른 캐스트 ... 제네릭을 사용하는 코드가 더 명확하고 안전합니다 ... 컴파일러는 컴파일시 런타임에 형식 제약 조건을 위반하지 않는지 확인할 수 있습니다 [강조 광산] 프로그램은 경고없이 컴파일되기 때문에 런타임에 ClassCastException을 발생시키지 않을 것이라고 확신 할 수 있습니다. 특히 대규모 프로그램에서 제네릭을 사용하면 얻을 수있는 순 효과는 개선 된 가독성 및 견고성. [강조 광산] "

2
Demi

jvm은 어쨌든 캐스트합니다 ... 그것은 암시 적으로 일반 유형을 "객체"로 취급하고 원하는 인스턴스화에 대한 캐스트를 생성하는 코드를 생성합니다. Java 제네릭은 단지 구문 설탕입니다.

2
Anonymous Coward

Mitchel이 지적했듯이 주요 장점은 여러 클래스를 정의 할 필요없이 강력한 타이핑을하는 것입니다.

이 방법으로 다음과 같은 작업을 수행 할 수 있습니다.

List<SomeCustomClass> blah = new List<SomeCustomClass>();
blah[0].SomeCustomFunction();

제네릭이 없으면 함수에 액세스하려면 blah [0]을 올바른 유형으로 캐스트해야합니다.

2
Kevin Pang

나는 이것이 C # 질문이라는 것을 알고 있지만 generics 는 다른 언어에서도 사용되며 사용법/목표는 매우 유사합니다.

Java 콜렉션은 Java 1.5 이후) generics 를 사용하므로이를 사용하는 좋은 장소는 고유 한 콜렉션 유사 오브젝트를 작성할 때입니다.

거의 모든 곳에서 볼 수있는 예는 두 개의 객체를 보유하고 있지만 일반적인 방식으로 해당 객체를 처리해야하는 Pair 클래스입니다.

class Pair<F, S> {
    public final F first;
    public final S second;

    public Pair(F f, S s)
    { 
        first = f;
        second = s;   
    }
}  

이 Pair 클래스를 사용할 때마다 처리하려는 객체의 종류를 지정할 수 있으며 모든 유형 캐스트 ​​문제는 런타임이 아닌 컴파일 타임에 표시됩니다.

제네릭은 키워드 'super'및 'extends'로 범위를 정의 할 수도 있습니다. 예를 들어, 제네릭 형식을 다루고 싶지만 Foo (setTitle 메서드가있는) 클래스를 확장하려는 경우 :

public class FooManager <F extends Foo>{
    public void setTitle(F foo, String title) {
        foo.setTitle(title);
    }
}

그 자체로는 그다지 흥미롭지는 않지만 FooManager를 다룰 때마다 MyClass 유형을 처리하고 MyClass가 Foo를 확장한다는 것을 아는 것이 유용합니다.

2
etchasketch

예를 들어 SpringORM과 Hibernate로 구현 된 GenericDao에서 이것을 사용합니다.

public abstract class GenericDaoHibernateImpl<T> 
    extends HibernateDaoSupport {

    private Class<T> type;

    public GenericDaoHibernateImpl(Class<T> clazz) {
        type = clazz;
    }

    public void update(T object) {
        getHibernateTemplate().update(object);
    }

    @SuppressWarnings("unchecked")
    public Integer count() {
    return ((Integer) getHibernateTemplate().execute(
        new HibernateCallback() {
            public Object doInHibernate(Session session) {
                    // Code in Hibernate for getting the count
                }
        }));
    }
  .
  .
  .
}

제네릭을 사용함으로써이 DAO의 구현은 개발자가 GenericDao를 서브 클래 싱함으로써 설계된 엔터티 만 전달하도록합니다.

public class UserDaoHibernateImpl extends GenericDaoHibernateImpl<User> {
    public UserDaoHibernateImpl() {
        super(User.class);     // This is for giving Hibernate a .class
                               // work with, as generics disappear at runtime
    }

    // Entity specific methods here
}

내 작은 프레임 워크가 더 강력합니다 (필터링, 게으른 로딩, 검색 등). 여기서는 예제를 제공하기 위해 단순화했습니다

나는 스티브와 너처럼 처음에 말했다 "너무 지저분하고 복잡한" 그러나 지금 나는 그것의 장점을 본다

1
victor hugo

제네릭을 사용하면 강력한 형식의 개체를 만들 수 있지만 특정 형식을 정의 할 필요는 없습니다. 가장 유용한 예는 List 및 유사한 클래스라고 생각합니다.

일반 목록을 사용하면 원하는대로 목록 목록 목록을 가질 수 있으며 항상 강력한 타이핑을 참조 할 수 있으며 변환이나 배열 또는 표준 목록과 같은 것을 할 필요가 없습니다.

1
Mitchel Sellers

제네릭을 사용하면 객체를 보유 할 수있는 객체 및 데이터 구조에 강력한 타이핑을 사용할 수 있습니다. 또한 일반 구조 (복싱/언 박싱)에서 객체를 검색 할 때 지루하고 값 비싼 타입 캐스트를 제거합니다.

둘 다 사용하는 한 가지 예는 연결된 목록입니다. Foo 객체 만 사용할 수 있다면 링크 된 목록 클래스는 어떤 것이 좋을까요? 모든 종류의 오브젝트를 처리 할 수있는 링크 된 목록을 구현하려면 가상 노드 내부 클래스의 링크 된 목록과 노드가 하나의 유형의 오브젝트 만 포함하도록하려면 일반이어야합니다.

1
Steve Landey

"유형 안전"및 "주조 없음"과 같은 명백한 이점이 이미 언급되었으므로 도움이되기를 바라는 다른 "이점"에 대해 이야기 할 수 있습니다.

우선, 제네릭은 언어 독립적 개념이며 IMO는 규칙적인 (런타임) 다형성을 동시에 생각하면 더 의미가 있습니다.

예를 들어, 객체 지향 디자인에서 알 수있는 다형성은 프로그램 실행이 진행되고 런타임 유형에 따라 관련 메소드가 호출 될 때 런타임에 호출자 객체가 계산되는 런타임 개념을 갖습니다. 제네릭에서는 아이디어가 다소 비슷하지만 모든 것이 컴파일 타임에 발생합니다. 그것은 무엇을 의미하며 어떻게 사용합니까?

(콤팩트하게 유지하기 위해 일반적인 방법을 고수합시다) 이는 이전에 다형성 클래스에서와 같이 별도의 클래스에서 동일한 방법을 사용할 수 있지만 이번에는 컴파일러에서 자동 생성되는 유형에 따라 다릅니다. 컴파일 타임에. 컴파일 타임에 제공하는 유형에 따라 메소드를 매개 변수화합니다. 따라서 런타임 다형성 (메소드 재정의)에서와 같이 메소드를 처음부터 새로 작성하는 대신 모든 단일 유형에 대해 작성하는 대신 컴파일러가 컴파일 중에 작업을 수행하도록 할 수 있습니다. 이는 시스템에서 사용될 수있는 모든 가능한 유형을 추론 할 필요가 없으므로 코드를 변경하지 않고도 훨씬 확장 성이 뛰어 나기 때문에 명백한 이점이 있습니다.

수업은 거의 같은 방식으로 작동합니다. 유형을 매개 변수화하고 코드는 컴파일러에 의해 생성됩니다.

"컴파일 시간"에 대한 아이디어를 얻으면 "바운드"유형을 사용하고 클래스/방법을 통해 매개 변수 유형으로 전달할 수있는 것을 제한 할 수 있습니다. 따라서 전달할 대상을 제어 할 수 있습니다. 이는 특히 다른 사람이 사용하는 프레임 워크를 가진 강력한 것입니다.

public interface Foo<T extends MyObject> extends Hoo<T>{
    ...
}

현재 아무도 MyObject 이외의 sth를 설정할 수 없습니다.

또한 메소드 인수에 유형 제한 조건을 "강제화"할 수 있습니다. 즉, 두 메소드 인수가 동일한 유형에 종속되도록 할 수 있습니다.

public <T extends MyObject> foo(T t1, T t2){
    ...
}   

이 모든 것이 의미가 있기를 바랍니다.

1
stdout

제네릭 (특히 컬렉션 /리스트와 함께)을 사용하면 얻을 수있는 또 다른 이점은 컴파일 시간 유형 검사입니다. 이것은 객체 목록 대신 일반 목록을 사용할 때 실제로 유용합니다.

1
Chris Pietschmann

컬렉션에 값 형식이 포함 된 경우 컬렉션에 삽입 될 때 개체에 상자를 열거 나 상자를 열 필요가 없으므로 성능이 크게 향상됩니다. resharper와 같은 멋진 애드온은 foreach 루프와 같이 더 많은 코드를 생성 할 수 있습니다.

1
gt124

가장 큰 이유는 Type safety

List<Customer> custCollection = new List<Customer>;

반대로

object[] custCollection = new object[] { cust1, cust2 };

간단한 예로서.

1
Vin

요약하면 제네릭을 사용하면 수행하려는 작업을보다 정확하게 지정할 수 있습니다 (더 강력한 입력).

여기에는 몇 가지 이점이 있습니다.

  • 컴파일러는 수행하려는 작업에 대해 더 많이 알고 있기 때문에 형식이 호환 가능하다는 것을 이미 알고 있기 때문에 많은 형식 캐스팅을 생략 할 수 있습니다.

  • 또한 프로그램의 수정에 대한 초기 피드백을받습니다. 이전에 런타임에 실패했을 경우 (예 : 원하는 유형으로 오브젝트를 캐스트 할 수 없었기 때문에) 이제 컴파일 타임에 실패하며 테스트 부서 파일에 논리적 버그 보고서를 작성하기 전에 실수를 수정할 수 있습니다.

  • 컴파일러는 권투 방지 등과 같은 더 많은 최적화를 수행 할 수 있습니다.

1
Thomas Danecker

추가/확장해야 할 몇 가지 사항 (.NET 관점에서 말하면) :

제네릭 형식을 사용하면 역할 기반 클래스와 인터페이스를 만들 수 있습니다. 이것은 이미 더 기본적인 용어로 말했지만 유형에 관계없이 구현되는 클래스를 사용하여 코드를 디자인하기 시작합니다.

메소드에 대한 일반적인 주장은 똑같은 일을 할 수 있지만, "Tell Do n't Ask"원칙을 캐스팅에 적용하는 데 도움이됩니다.

1
SpongeJim

컬렉션에 제네릭을 사용하는 것은 간단하고 깨끗합니다. 당신이 다른 곳에서 그것을 펀칭하더라도 컬렉션의 이익은 나에게 승리입니다.

List<Stuff> stuffList = getStuff();
for(Stuff stuff : stuffList) {
    stuff.do();
}

vs

List stuffList = getStuff();
Iterator i = stuffList.iterator();
while(i.hasNext()) {
    Stuff stuff = (Stuff)i.next();
    stuff.do();
}

또는

List stuffList = getStuff();
for(int i = 0; i < stuffList.size(); i++) {
    Stuff stuff = (Stuff)stuffList.get(i);
    stuff.do();
}

이것만으로도 제네릭의 한계 "비용"의 가치가 있으며, 이것을 사용하고 가치를 얻는 데 일반 전문가가 될 필요는 없습니다.

0
Will Hartung

제네릭은 또한 유형별 지원을 제공하면서 더 재사용 가능한 객체/메서드를 생성 할 수있는 기능을 제공합니다. 경우에 따라 많은 성능을 얻을 수도 있습니다. Java Generics에 대한 전체 사양을 모르지만 .NET에서 인터페이스 구현, 생성자 및 파생 구현과 같은 Type 매개 변수에 대한 제약 조건을 지정할 수 있습니다.

0
Eric Schneider

한 번이 주제에 대해 이야기했습니다. http://www.adventuresinsoftware.com/generics/ 에서 내 슬라이드, 코드 및 오디오 녹음을 찾을 수 있습니다.

0
Michael L Perry