Whiteship's Note


Generics 번외 - 겉모습만 보곤 알 수 없슴.

Java : 2007.01.17 12:47


"와일드 카드를 사용한 메소드 내에서 Collection에 어떤 요소를 추가하는 일은 컴파일 에러를 발생합니다."

하지만 겉모습으로 판단할 수 없습니다. Collection에 다음과 같은 인터페이스가 있습니다.

    boolean addAll(Collection<? extends E> c);

"Collection을 매개변수로 받아서 그 안에 들어있는 어딘가에 집어 넣으려는 매소드 같은데 와일드 카드를 쓰고 있자나.. 어허.. 이거 뭐지? 이게 되나??" 라는 생각을 했었습니다.

그래서 이것을 구현한 코드를 찾아 봤습니다.

public boolean addAll(Collection<? extends E> c) {

       boolean modified = false;

       Iterator<? extends E> e = c.iterator();

       while (e.hasNext()) {

           if (add(e.next()))

             modified = true;

       }

       return modified;

    }

Collection 인터페이스를 구현하고 있는 AbstractCollection 클래스에 있는 소스코드 입니다.

Collection 인터페이스에 있는 add() 메소드를 보지 않는 이상 알 수가 없겠네요. 위 메소드 내에서는 추가하는 일을 add() 메소드에게 넘기고 있기 때문이지요.

사용자 삽입 이미지

Collection 인터페이스에 있는 add는 E라는 type parameter를 사용해서 특정 타입만으로 제한을 했기 때문에 추가하는 작업은 무사히 진행 되겠군요.

실제 ArrayList에서 add(E)를 구현한 코드는 다음과 같습니다.

public boolean add(E o) {

       ensureCapacity(size + 1);  // Increments modCount!!

       elementData[size++] = o;

       return true;

    }

'Java' 카테고리의 다른 글

private에 대한 착각  (2) 2007.03.05
Stream 인코딩 바꾸기  (0) 2007.02.28
SWT 프로그램 실행하기  (0) 2007.02.21
제8회 한국 자바 개발자 컨퍼런스  (2) 2007.01.25
GC관련 아티클  (0) 2007.01.19
Generics 번외 - 겉모습만 보곤 알 수 없슴.  (2) 2007.01.17
Generics  (2) 2007.01.17
Eclipse 단축키 모음  (6) 2007.01.11
Generic과 다형성 2탄  (4) 2007.01.05
Generic과 다형성  (0) 2007.01.05
자바 검은 띠에 도전해 보시길~  (2) 2006.12.31
top

  1. Favicon of http://younghoe.info/ BlogIcon 영회 2007.01.17 13:18 PERM. MOD/DEL REPLY

    '겉모습으로 판단할 수 없습니다.'

    좋은 표현이군. :)

    Favicon of http://whiteship.tistory.com/ BlogIcon 기선 2007.01.17 15:10 PERM MOD/DEL

    ㅎㅎ 감사합니다. :)

Write a comment.


Generics

Java : 2007.01.17 12:26


참조 :

1. Introduction
자바5.0에 새로 추가된 기능중에 하나로 이게 뭔지 한 문장으로 표현하면 다음과 같습니다.
Generics allow you to abstract over types.

Generic을 사용하는 가장 좋은 예로 Collection을 들 수 있겠습니다.

List myIntList = new LinkedList(); // 1
myIntList.add(new Integer(0)); // 2
Integer x = (Integer) myIntList.iterator().next(); // 3

이러한 기존의 코드를 다음과 같이 사용할 수 있습니다.

List<Integer> myIntList = new LinkedList<Integer>(); // 1’
myIntList.add(new Integer(0)); //2’
Integer x = myIntList.iterator().next(); // 3’

2’ 에서 Integer가 아닌 다른 객체를 넣으려고 하면 컴파일 에러가 발생합니다. 3’ 에서는 캐스팅이 필요없습니다.

기존의 List는 모든 객체가 Object 타입으로 들어가고 그래서 꺼낼 때 캐스팅이 필요했지만 Generic을 사용한 List를 사용하면 type-parameter를 사용해서 특정 타입의 객체만 들어갈 수 있도록 지정할 수 있기 때문에 꺼낼 때 캐스팅이 필요 없습니다.

=> 코드의 가독성과 견고함(robustness)을 높여줍니다.

2. Defining Simple Generics

public interface List<E> {
    void add(E x);
    Iterator<E> iterator();
}
public interface Iterator<E> { E next();
    boolean hasNext();
}

위와 같이 생겼으며 사용하는 방법은 1. Intoduction에서 보았듯이 List<Integer>처럼 E와 같은 type-parameter 대신에 원하는 실제 타입(actual type parameter)를 넣어 주면 됩니다.

이 때 다음과 같은 코드가 생길 것이라고 생각하는 것은 괜찮지만 사실은 그렇치 않습니다.

public interface IntegerList {
void add(Integer x)
Iterator<Integer> iterator();
}

왜냐면 List<Integer>가 저런 코드를 사용하는 것 처럼 동작하긴 하지만 실제 저런 복사본을 어디에도 만들지 않기 때문입니다. C++의 탬플릿과는 다르다고 합니다.

Agile Java 14장 5절에 보면 C++은 인수화된 형(type-parameter)을 사용할 때마다 새로운 형식을 만든다고 합니다. 하지만 자바는 Erasure(삭제) 방식을 사용한다고 합니다. 즉 인수화된 형식 정보를 지우고 적절한 캐스팅으로 대체 된다고 합니다.

3. Generics and Subtyping
List<String> ls = new ArrayList<String>(); //1
List<Object> lo = ls; //2

위 두 줄의 코드 중에서 1번은 문제가 없고 2번은 컴파일 에러가 발생합니다. 다음과 같은 일이 발생 할 수 있기 때문에 미연에 방지하기 위함입니다.

lo.add(new Object()); // 3
String s = ls.get(0); // 4: attempts to assign an Object to a String!

4. Wildcards
void printCollection(Collection c) {
    Iterator i = c.iterator();
    for (k = 0; k < c.size(); k++) {
        System.out.println(i.next());
}}

위와 같은 코드를 Generic을 사용하여 다음과 같이 표현하고 싶겠지만...

void printCollection(Collection<Object> c) {
    for (Object e : c) {
        System.out.println(e);
}}

원하는 대로 동작하지 않을 것입니다. 오로지 Object만 들어있는 컬렉션을 받을 수 있을 뿐... List<String>을 printCollection의 매개 변수로 넣으려고 하면 컴파일 에러가 발생합니다. 3. Generics and Subtyping에서 발생했던 문제와 같은 논리입니다.

Collection<Object>는 모든 타입들의 Supertype이 아닙니다. 모든 타입들의 Supertyle은 ? 와일드 카드를 사용해서 표현합니다. 즉 Collection<?> 이렇게 표현하면 됩니다. 따라서 위의 메소드를 다음과 같이 작성하면 원하는 결과를 얻을 수 있습니다.

void printCollection(Collection<?> c) {
    for (Object e : c) {
        System.out.println(e);
}}

주의할 것은 ? 을 사용한 콜렉션에 추가하는 일을 하면 컴파일 에러가 발생한다는 것입니다.

Collection<?> c = new ArrayList<String>();
c.add(new Object()); // compile time error

왜냐면.. ? 는 모든 타입을 나타내기 때문에 Collection에 어떤 타입이 들어갈지 예측할 수 없기 때문입니다. 1. Introduction에서 Collection에 타입의 제약을 가해서 빼낼 때 캐스팅 없이 알고 있는 타입으로 빼내는 견고함과 가독성을 위해 Generics를 사용하기 시작한 것을 기억하실 것입니다. 그렇다면 지금 벌어지는 일이 그와는 정반대 되는 일임을 알 수 있을 것입니다. 따라서 컴파일 에러가 발생합니다.

넣는 것과 다르게 ? 를 사용한 Collection에서 꺼내는 일(get())은 가능합니다. 위와 같은 코드의 경우 나오는 객체의 타입을 Object 타입으로 예상하고 있고 실제 모든 객체들은 Object 타입이기 때문입니다.

? 의 기본 상위 경계는 Object 이기 때문이라고 해도 되겠네요. 조금 후에 extends를 사용해서 상위 경계를 원하는 타입으로 제한할 수 있는데 그때는 나오는 타입을 역시 원하는 상위 타입으로 예측할 수 있기 때문에 get()을 하는데는 아무 문제가 없습니다.

4.1 Bounded Wildcards

사용자 삽입 이미지
위와 같은 구조에서 Canvas의 drawAll의 코드가 다음과 같다고 생각해 봅니다.

public void drawAll(List<Shape> shapes) {
    for (Shape s: shapes) {
       s.draw(this);
   }
}

이 때 drawAll의 매개 변수로 넘겨줄 수 있는 Collection은 오로지 List<Shape> 밖에 없습니다. 하지만 메소드 선언 부를 다음과 같이 바꾸면 public void drawAll(List<? extends Shape> shapes) { ... } List<Circle>과 List<Rectangle>도 매개변수로 넘겨 줄 수 있습니다.

<? extends Shape>와 같은 표현을 상위 경계[각주:1]을 가했다고 해서 bounded wildcard 라고 하며 Shape를 상위 경계(upper bound)라고 합니다.

이때도 역시 4. Wildcards 에서 보았던 것처럼 다음의 코드는 컴파일 에러를 발생합니다.

public void addRectangle(List<? extends Shape> shapes) {
   shapes.add(0, new Rectangle()); // compile-time error!
}

? 의 상위 제한은 알지만 여전히 어떤 객체가 들어갈지는 예측할 수가 없습니다. List<Rectangle> 에 Rectangle의 상위 타입(Shape의 하위타입이면서..)이 들어 갈 수도 있기 때문입니다.

5 Generic Methods
static void fromArrayToCollection(Object[] a, Collection<?> c) {
    for (Object o : a) {
        c.add(o); // compile time error
}}

배열에 있는 요소들을 콜렉션으로 옮겨 담는 메소드인데 위의 메소드에서 컴파일 에러가 발생하는 이유는 앞에서도 설명을 했지만 ? 와일드 카드는 모든 타입을 나타내기 때문에 Collection이 어느 특정 타입을 유지 하리라 예측 할 수 없기 때문입니다.

static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
    for (T o : a) {
        c.add(o); // correct
}}

하지만 위 코드는 에러가 발생하지 않습니다. T라는 특정 타입 하나로 제한되었기 때문입니다. 이런 메소드를 generic method라고 합니다. 위 메소드를 사요하면 특정 타입만을 가지는 Collection을 만들 수 있기 때문에 Generics를 사용하는 이유인 견고함과 가독성 유지에 위배되지 않습니다.

그렇다면 문제는 언제 ? 를 쓰고 언제 generic method를 사용하는가 입니다.

interface Collection<E> {
    public boolean containsAll(Collection<?> c);
    public boolean addAll(Collection<? extends E> c);
}

위 코드를 아래 처럼 generic method를 사용해서 표현할 수도 있습니다.

interface Collection<E> {
    public <T> boolean containsAll(Collection<T> c);
    public <T extends E> boolean addAll(Collection<T> c);
// hey, type variables can have bounds too!
}

하지면 여기서 사용된 T라는 type parameter는 리턴타입에 영향을 주지도 않으며 매개변수들 간의 계층 관계를 나타내지도 않습니다.[각주:2] 이런 경우에서 addAll에서의 E는 상위제한을 가하는 용도로만 사용되었습니다. 또한 다양한 type을 받아 들이기 위해 T라는 type parameter를 사용했습니다. 이럴때는 ? 와일드카드를 사용하는 것이 좋습니다.
  • 다형성을 나타내고 싶고 매개 변수에서 한번만 사용되는 type parameter는 generic method보다 ? 와일드 카들르 사용하는 것이 좋습니다.
  • generic method는 매개변수 타입들 간 or/and 리턴 타입 간의 관계를 표현할 때 사용하는 것이 좋습니다.
주의 할 것은 아래와 같이 매개변수 간의 관계가 있다고 해서

class Collections {
    public static <T> void copy(List<T> dest, List<? extends T> src){...}
}

아래와 같이 S라는 type parameter를 상용할 수도 있습니다.

class Collections {
    public static <T, S extends T> void copy(List<T> dest, List<S> src){...}
}

하지만 여기서 매개변수들 만 본다면 T라는 type parameter는 두 개의 매개변수에서 모두 사용된 것이지만[각주:3] S는 하나의 매개변수에서 사용된 것이나 마찬가지 입니다. 따라서 이 경우도 ? 와일드 카드를 사용하는 것이 보다 깨끗하고 간결합니다.


참고 할 곳(물개 선생님께서 정리해 두신 링크들)
- SUN 기본 튜토리얼 : java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf
- 이희승님 정리 문서 : trustin.googlepages.com/Java5Generics.pdf
- Toby님 JavaGeneric과 Erasure : toby.epril.com/?p=248
- advanced: http://blog.interface21.com/main/2007/01/16/a-bridge-too-far/
  1. 기본 상위 경계는 Object 입니다. [본문으로]
  2. 물론 여기서 매개변수는 한 개 밖에 없어서 더욱 그렇쵸. [본문으로]
  3. S = S extends T 이기 때문에 S를 대체하면 T가 두번 사용된 것입니다. [본문으로]

'Java' 카테고리의 다른 글

Stream 인코딩 바꾸기  (0) 2007.02.28
SWT 프로그램 실행하기  (0) 2007.02.21
제8회 한국 자바 개발자 컨퍼런스  (2) 2007.01.25
GC관련 아티클  (0) 2007.01.19
Generics 번외 - 겉모습만 보곤 알 수 없슴.  (2) 2007.01.17
Generics  (2) 2007.01.17
Eclipse 단축키 모음  (6) 2007.01.11
Generic과 다형성 2탄  (4) 2007.01.05
Generic과 다형성  (0) 2007.01.05
자바 검은 띠에 도전해 보시길~  (2) 2006.12.31
Hiding Method  (0) 2006.12.31
top

TAG generics
  1. Favicon of http://seal.tistory.com BlogIcon 물개선생 2007.01.17 08:40 PERM. MOD/DEL REPLY

    오홋.. 너무 멋지다. 어제 오후에 자료를 전해 드렸는데, 오늘 아침에 정리되어 있다니. 흠.. 최상의 공부파트너를 만난 게로군요. 제게도 좋은 자극이 됩니다. 화이팅~~

    Favicon of http://whiteship.tistory.com/ BlogIcon 기선 2007.01.17 09:47 PERM MOD/DEL

    넵 ^^ 감사합니다.
    좋은 자료를 주셔서 도움이 많이 됐습니다.
    5장 뒷 부분은 이해가 덜 되서요~ 이따 학교가서 더 읽어봐야겠어요. :)

Write a comment.