"안녕, 아미고!"

"안녕, 엘리!"

"오늘은 리시와 제가 제네릭에 대한 모든 것을 알려드리겠습니다."

"잠깐, 나는 이미 거의 모든 것을 알고 있는 것 같아."

"거의 전부지만 전부는 아닙니다."

"정말요? 알겠습니다. 들을 준비가 되었습니다."

"그럼 시작하겠습니다."

"Java에서 제네릭은 유형 매개변수가 있는 클래스입니다."

"제네릭이 발명된 이유는 코드의 주석을 참조하십시오."

ArrayList stringList = new ArrayList();
stringList.add("abc"); // Add a string to the list
stringList.add("abc"); // Add a string to the list
stringList.add( 1 ); // Add a number to the list

for(Object o: stringList)
{
 String s = (String) o; // There will be an exception here when we get to the integer
}

제네릭을 사용하여 문제를 해결하는 방법:

ArrayList<String> stringList = new ArrayList<String>();
stringList.add("abc"); // Add a string to the list
stringList.add("abc"); // Add a string to the list
stringList.add( 1 ); // There will be a compilation error here

for(Object o: stringList)
{
 String s = (String) o;
}

"이 코드는 컴파일되지 않으며 잘못된 데이터 유형을 추가하여 발생하는 오류는 컴파일 중에 발견됩니다."

"예, 이미 알고 있습니다."

"좋아, 좋아. 반복은 배움의 어머니다."

"하지만 Java의 제작자는 제네릭을 만들 때 약간 게을렀습니다. 매개 변수로 본격적인 유형을 만드는 대신 매끄러운 최적화에 빠졌습니다. 실제로는 제네릭에 유형 매개 변수에 대한 정보를 추가하지 않았습니다. 대신 모든 컴파일하는 동안 마법이 발생합니다."

제네릭을 사용한 코드
List<String> strings = new ArrayList<String>();
strings.add("abc");
strings.add("abc");
strings.add( 1); // Compilation error

for(String s: strings)
{
 System.out.println(s);
}
실제로 일어나는 일
List strings = new ArrayList();

strings.add((String)"abc");
strings.add((String)"abc");
strings.add((String) 1); // Compilation error

for(String s: strings)
{
 System.out.println(s);
}

"깔끔하네."

"예, 하지만 이 접근 방식에는 부작용이 있습니다.  유형 매개변수에 대한 정보는 일반 클래스 내에 저장되지 않습니다. 이 접근 방식은 나중에 유형 삭제  로 알려지게 되었습니다 ."

"즉, 유형 매개변수가 있는 고유한 클래스가 있는 경우 클래스 내에서 해당 클래스에 대한 정보를 사용할 수 없습니다."

제네릭을 사용한 코드
class Zoo<T>
{
 ArrayList<T> pets = new ArrayList<T>();

 public T createAnimal()
 {
  T animal = new T();
  pets.add(animal)
  return animal;
 }
}
실제로 일어나는 일
class Zoo
{
 ArrayList pets = new ArrayList();

 public Object createAnimal()
 {
  Object animal = new ???();
  pets.add(animal)
  return animal;
 }
}

"컴파일하는 동안 모든 매개변수 유형이 Object로 대체됩니다. 그리고 클래스 내부에는 전달된 유형에 대한 정보가 없습니다."

"예, 동의합니다. 그게 최고가 아닙니다."

"그렇게 무섭지 않아. 나중에 이 문제를 해결하는 방법을 알려줄게."

하지만 더 있습니다. Java를 사용하면 유형 매개변수의 상위 유형을 지정할 수 있습니다. 이를 위해 extends 키워드가 사용됩니다. 예를 들어:

제네릭을 사용한 코드
class Zoo<T extends Cat>
{
 T cat;

 T getCat()
 {
  return cat;
 }

 void setCat (T cat)
 {
  this.cat = cat;
 }

 String getCatName()
 {
  return this.cat.getName();
 }
}
실제로 일어나는 일
class Zoo
{
 Cat cat;

 Cat getCat()
 {
  return cat;
 }

 void setCat(Cat cat)
 {
  this.cat = cat;
 }

 String getCatName()
 {
  return this.cat.getName();
 }
}

"두 가지 사실에 유의하십시오."

"첫째, 어떤 유형도 매개변수로 전달할 수 없습니다. Cat 또는 Cat을 상속하는 클래스만 전달할 수 있습니다."

"두 번째로, Zoo 클래스 내에서 T 유형의 변수는 이제 Cat 클래스의 메서드를 호출할 수 있습니다.  오른쪽 열에 그 이유가 설명되어 있습니다(T가 있는 모든 곳에서 Cat이 대체되기 때문입니다)."

"예. Cat 또는 Cat의 하위 클래스가 유형 인수로 전달된다고 하면 유형 T가 항상 Cat 클래스의 메서드를 가질 것이라고 확신합니다."

"글쎄요, 똑똑하군요."