"안녕, 아미고!"
"안녕, 엘리!"
"오늘은 리시와 제가 제네릭에 대한 모든 것을 알려드리겠습니다."
"잠깐, 나는 이미 거의 모든 것을 알고 있는 것 같아."
"거의 전부지만 전부는 아닙니다."
"정말요? 알겠습니다. 들을 준비가 되었습니다."
"그럼 시작하겠습니다."
"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 클래스의 메서드를 가질 것이라고 확신합니다."
"글쎄요, 똑똑하군요."
GO TO FULL VERSION