제 홈페이지의 모든 글은 anti-nhn license에 따릅니다.



java Generics 6 - 기존 코드와의 호환

지금까지는 generic을 지원하는 버젼에 대해서만 알아보았다. 하지만 이미 기존에 만들어진 소스가 있을 것이다. 기존에 짜여진 모든 소스 코드를 수정하는 것은 있을 수 없는 일이다.
10장에서 기존코드를 generic을 사용하는 새로운 코드로 바꾸는 것에 대해 알아보도록 하겠다. 이번 장에서는 훨씬 간단한 문제인 기존 소스 코드와의 호환성에 대해서만 알아볼 것이다. 이것은 두 부분으로 나뉘어 진다. 기존 코드를 generic이 사용된 코드에서 사용하는 것과 generic이 들어간 코드를 기존 코드에서 사용하는 것이다.

기존 코드를 generic 코드에서 사용하기.

generic의 장점을 충분히 살리면서 기존 코드를 사용하는 법을 알아보겠다.
예제로 com.Fooblibar.widgets 패키지를 사용하고자 한다 치자.(Fooblibar가 뭐 하는 건지는 알 필요 없으므로 생략!)

package com.Fooblibar.widgets;
public interface Part { ...}
public class Inventory { /**
* Adds a new Assembly to the inventory database.
* The assembly is given the name name, and consists of a set
* parts specified by parts. All elements of the collection parts
* must support the Part interface.
**/
    public static void addAssembly(String name, Collection parts) {...} 
    public static Assembly getAssembly(String name) {...}
}
public interface Assembly { Collection getParts(); // Returns a collection of Parts
}


이제 위에서 소개한 API를 사용하는 코드를 짠다고 가정하자. addAssembly() 메쏘드를 호출할 때 적절한 인자로 호출하는 게 바람직할 것이다. 그러니까 Collection<Part>라고 정의된 generic type이 명시적인 인자로 호출을 하고자 한다.

package com.mycompany.inventory;
import com.Fooblibar.widgets.*;
public class Blade implements Part {
...
}
public class Guillotine implements Part {
}
public class Main { 
    public static void main(String[] args) { 
        Collection<Part> c = new ArrayList<Part>();
        c.add(new Guillotine()) ;
        c.add(new Blade());
        Inventory.addAssembly(”thingee”, c);
        
Collection<Part> k = Inventory.getAssembly(”thingee”).getParts();
    }
}

addAssembly를 호출할 때 두 번째 인자는 Collection 타입이다. 그리고 실제로 넘기는 인자는 Collection<Part> 타입이다. 어떻게 작동할까? 컴파일러는 Collection이 어떤 타입으로 지정될지 모른다.
Collection과 같은 generic type이 타입인자 없이 호출 될 때는 row-type으로 호출된다.
이미 살펴봤던 것처럼 Collection은 Collection<?>의 의미이지 Collection<Object>를 의미하는 게 아니다라고 생각할 것이다.
그러나 위의 코드를 보면 별로 그런 것 같지도 않다. 
Collection<Part> k = Inventory.getAssembly(”thingee”).getParts();
가 정상적으로 작동하고 있기 때문이다. Collection<?>은 Collection<Part>로 명시적인 캐스팅없이 할당이 불가능하다. 즉, Collection<?>로 호출이 되었다면 에러가 났어야 마땅하다.
이러한 것은 정상적으로 작동한다. 다만 "unchecked warning"(@로 시작하는 annotation 중 하나) 이 없다면, warning이 생긴다. 컴파일러는 정합성을 보장해 줄 수 없기 때문이다. getAssembly()라는 기존에 이미 존재하던 메쏘드가 Collection<Part>를 리턴할 지를 체크할 방법이 없다. 위에서 사용한 코드에서는 단지 Collection이라고만 되어 있고, 그런 경우 모든 object를 사용할 수 있게 하는 방법뿐이다.
이론적으로만 봤을 때 이건 별로 합당치 못하지만, 기존에 쓰던 코드를 버리라고 할 수는 없다. 이건 개발자의 몫이다.
raw type이란 결국 wildcard와 매우 흡사하지만, raw type은 타입체크를 하지 않는 것이다. 이것은 기존 코드와의 호환성을 고려한 결정이다.
generic 코드에서 기존의 코드를 호출하는 것은 위험하다. 타입에 대한 안정성을 어떻게도 보장할 수 없기 때문이다.


Erasure 와 Translation

public String loophole(Integer x) { 
    List<String> ys = new LinkedList<String>();
    List xs = ys;
    xs.add(x); // 컴파일 시점 unchecked warning
    return ys.iterator().next();
}


String 리스트를 옛날 방식의 리스트에다가 집어 넣으려 한다. 컴파일 시점에 warning이 뜬다.
이번에는 Integer를 리스트에 넣고 String으로 뽑아 쓰려는 경우를 보자. 물론, 잘못된 시도다. Warning을 무시하고 일단 실행시켜보면 잘못된 타입으로 뽑아 쓸 때 에러가 난다.

public String loophole(Integer x) { 
    List ys = new LinkedList();
    List xs = ys;
    xs.add(x);
    return (String) ys.iterator().next(); // 런 타임 에러.
}


리스트로 부터 요소를 뽑아와 String으로 캐스팅을 하려고 하면 ClassCastException이 발생하게 된다.
컴파일러는 erasure라는 generics에 대한 첫 단계 변환을 실행한다. 소스 코드 차원에서의 변환이라고 생각하면 대략 맞다. 이는 generic을 제거하는 작업이라고 볼 수 있다. (제 생각에는 Just In Time에서 제거한다는 의미같습니다. 자바는 2단계 컴파일을 하는데, 첫단계가 우리가 아는 .class를 생성해 내는 것이고, 두번째 단계가 컴퓨터에서 돌아갈 수 있는 머쉰 코드를 만드는 것인데, 그 두번째 단계를 말하는 것 같습니다.)

erasure는 generic type 정보를 전부 제거해버리는 작업이다. List<String>을 List로 바꿔버리는 것처럼 <> 사이에 감싸진 모든 정보를 날려버린다.

기존 코드에서 generic 코드 사용하기


반대의 경우를 생각해보자. Fooblibar.com의 API를 generics를 사용하도록 바꿔보자. 그러나 그 API를 쓰는 코드는 아직 generics를 사용하지 않는다고 하면 아래와 같은 코드가 나올 것이다.

package com.Fooblibar.widgets;
public interface Part { ...} 
public class Inventory { 
    /**
    * Adds a new Assembly to the inventory database.
    * The assembly is given the name name, and consists of a set
    * parts specified by parts. All elements of the collection parts
    * must support the Part interface.
    **/    
    public static void addAssembly(String name, Collection<Part> parts) {...} 
    public static Assembly getAssembly(String name) {...}
}
public interface Assembly { 
    Collection<Part> getParts(); // Returns a collection of Parts
}


그걸 사용하는 코드는 다음과 같을 것이다.

package com.mycompany.inventory;
import com.Fooblibar.widgets.*;
public class Blade implements Part {
}
public class Guillotine implements Part {
}
public class Main { 
    public static void main(String[] args) { 
        Collection c = new ArrayList();
        c.add(new Guillotine()) ;
        c.add(new Blade());
        Inventory.addAssembly(”thingee”, c); // 1: unchecked warning
        Collection k = Inventory.getAssembly(”thingee”).getParts();
    }
}


위의 코드는 generics가 나오기 전에 짜여졌으며, com.Fooblibar.widgets와 collection 라이브러리(둘 다 generics를 쓴다) 를 사용하고 있다. 위의 코드에서 generics가 사용되어야 할 부분은 전부 raw type으로 정의되어 있다.

첫번째 줄에서 Collection<Part>를 사용해야하는데 raw type의 Collection이 호출되므로 unchecked warning이 발생한다. 컴파일러 입장에서는 이 Collection이 Collection<Part>라고 확인할 방법이 없다.


java Generics 7 - Fine Print

by 삼실청년 | 2008/04/16 17:26 | 컴터질~ | 트랙백(1) | 핑백(2) | 덧글(0)

트랙백 주소 : http://iilii.egloos.com/tb/4295940
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
Tracked from at 2014/03/11 00:42

제목 : garcinia cambogia extract
line4...more

Linked at 건실성실착실 3실 청년! : .. at 2010/11/05 17:41

... 위와 같이 여러 개의 ?들끼리 굳이 구별을 할 필요가 있다면 ?대신 T,S와 같은 파라미터 변수를 사용하면 된다. 위와 같이 중첩된 경우도 마찬가지다.java Generics 6 - 기존 코드와의 호환 ... more

Linked at 건실성실착실 3실 청년! : .. at 2013/05/06 06:06

... 이유가 없는 곳이죠! 3가지 포인터가 있는데, 첫째는 Varargs ( T... 으로 표현된 부분) , 둘째는 erasure (generic에서 생기는 거. 자세한 건 요기), 세째는 bridge method 입니다. line 29에서 Varargs 는 내부적으로 갯수가 변하는 변수로 처리하는 것이 아니라 Array로 처리 ... more

:         :

:

비공개 덧글

◀ 이전 페이지          다음 페이지 ▶