http://chans.tistory.com/58

java.sun.com에 아래와 같은 java의 Gerneric에 대한 설명 문서가 있습니다.

Generics in the Java Programming Language

--------------------------------------------------------------------
아래는 문서를 읽고 그냥 적어본 것이니 내용이 반드시 정확한 것은 아닙니다. 틀린 부분이 있다면 지적해주세요.


위 문서는 java의 generics에 대한 설명입니다.

JDK 1.5부터 Gernerics라는 개념이 추가되었습니다. type을 명확하게 정하지 않고도 type을 처리할 수 있는 C++의 템플릿과 비슷한 기능으로 사용법은 비슷합니만 다른 점은 <> 사이에 들어가는 type의 수만큼 class를 만드는 C++과는 달리 java에서는 하나의 class로 여러 type들을 처리합니다 (java compiler에서 어떻게 처리하는지는 더 깊은 내용이라 파악해보진 않았습니다).

상속을 많이 사용하는 java에서는 매우 유용할 기능을 보이지만, JDK 1.5 아래 버전들에서 사용하던 소스, 라이브러리들과 같이 사용하는 것은 주의가 많이 필요할 것으로 보입니다.  

Object를 이용하여 method의 파라미터나 리턴값을 처리하는 경우 compile time에서는 type 검사가 어려웠으나, gernerics를 사용하면 compile time에서 type 검사가 수행됩니다.

추가로 크기가 큰 소스인 경우에 가독성과 안정성이 증가한다고 본문에 나와있습니다 (아직 해보진 않았으니 그런가 보다 합니다).


본문의 간단한 사용예:
List<Integer> myIntList = new LinkedList<Integer>();
myIntList.add(new Integer(3));
Integer x = myIntList.iterator().next();
여기에 사용된 "List<Integer>" 형태는 parameterized type 이라고 합니다.

본문의 다른 사용예:

public interface List<E> {
   void add(E x);
   Iterator<E> iterator();
}
여기에 사용된 "List<E>" 형태는 formal type parameter 라고 합니다.


type parameter로 E, T, S 등의 한글자로된 대문자를 주로 사용하는데 기존의 type과 구분하기 위해서 입니다.


(1)

List<String> ls = new LinkedList<String>();
List<Object> lo = ls;
(1)의 코드는
(2)

lo.add(new Object());
String s = lo.get(0);  // (3)
(2)의 코드와 같이 사용할 경우 (3)에서 Object type을 String 변수에 할당하려고 했기 때문에 compile error 가 발생합니다.


Collection의 모든 요소를 출력하는 것은 아래의 코드로 할 수 있습니다.
void printCollection(Collection<?> c) {
   for (Object o : c)
       System.out.println(e);
   }
}
<?>와 같이 사용하는 것을 wildcard type이라고 합니다. 어떤 type이라도 들어올 수 있다는 의미입니다. Object는 java에서 모든 class의 최상위 class이기 때문에 위의 코드는 type safe 합니다.

Collection<?> c = new ArrayList<String>();
c.add(new Object()); // (4),  compile time error
위의 코드는 c가 어떤 type인지 알 수 없는데 Object type 값을 할당하려고 하기 때문에 compile error가 발생합니다. c의 subtype을 할당해야 하는데 최상위인 Object를 할당하려고 했기 때문이라고 이해했습니다.



Shape의 child로 Circle, Rectangle class를 만들고 Canvas에서 모든 도형 list를 출력하는 경우는 다음과 같이 사용합니다.

본문 예제 참고:
public class Canvas {
   public void drawAll(List<? extends Shape> shapes) {
       for (Shape s : shapes)
           s.draw(this);
   }
}
<? extends T>와 같은 형태를 upper bounded wildcard라고 합니다. T의 child type만을 사용할 수 있다는 것을 의미합니다.
위의 경우엔 drawAll()의 argument로 List<Circle>, List<Rectangle>, List<Shape>가 모두 올 수 있습니다.

그러나 아래와 같은 코드는 compile error가 납니다.
public void addRectangle(List<? extends Shape> shapes) {
   shapes.add(0, new Rectangle()); // compile error
}
addRectangle의 argument로 List<Shape>가 넘어올 수 있는데 거기에 Rectangle type의 값을 할당하려고 하기 때문에 compile error가 납니다.



method 선언부에 generic 을 사용하는 것을 generic method라고 합니다.
static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
   for (T o : a)
       c.add(o);
}
method 앞에 <T>라고 해두면 method의 다른 부분에서 T type을 사용할 수 있습니다.


다음과 같이 사용할 수도 있습니다. 다음의 (5)와 (6)은 같은 내용을 다르게 표기한 것입니다. (5)는 generic method 와 wildcard를 같이 사용한 것입니다.
(5)

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

(6)

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

다음은 wildcard와 generic method로 같은 내용을 표기한 것입니다.
(7)

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


(8)


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

어떨 때 wildcard를 쓰고, 어떨 때 generic method를 사용할까?
- generic method는 method에서만 사용할 수 있다. 그외의 경우는 wildcard를 사용한다.
- method 내에서도 복잡하지 않게 한군데에서만 typed parameter가 사용되는 경우는 wildcard를 사용하는 것이 좋다. (generic method는 보기가 복잡하다 <- 취향일지도...)
- type들 사이에 구분이 명확하게 필요한 경우(method 여기 저기에 "?"가 있으면 헷갈릴 듯함)는 generic method를 사용하자.


기존의 Legacy code(generic을 사용하지 않는 code)를 generic code에서 사용하는 예입니다.
(Legacy Code)

package com.Fooblibar.widgets;

public interface Part { ... }
public class Invectory {
   public static void addAssembly(String name, Collection parts) { ... }
   public static Assembly getAssembly(String name) { .... }
}
public interface Assembly {
   Collection getParts(); // returns a collection Parts
}
(Generic code)

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("things", c);
(9)   Collection<Part> k = Inventory.getAssembly("things").getParts();
   }
}
Generic code 관점에서 Legacy code의 Collection과 같이 <>없는 type을 raw type 이라고 합니다. 개념상으로는 Collection<?>와 같이 unknown type으로 생각하면 될 것 같습니다. compile은 되지만 위의 예에서 (9)의 위치에서 unchecked waring이 뜹니다. method의 argument로 넘어갈 때는 Collection<?>로 보고, (9)와 같이 리턴 타입으로 받을 때는 내부적으로 Collection<Part>로 해석해 준다고 합니다.

기존의 Legacy code와 함께 사용할 경우에는 unchecked warning에 대해서 매우 주의해서 사용해야 하고, 여러 가지 문제들은 사실상 프로그래머가 해결해 줘야 합니다.


public String loophole(Integer x) {
   List<String> ys = new LinkedList<String>();
   List xs = ys;
   xs.add(x); // compile time uncheced waring
   return ys.iterator().next(); // (10)
}
위의 코드는 compile 후엔 내부적으로 다음과 같이 바뀝니다.
public String loophole(Integer x) {
   List ys = new LinkedList();
   List xs = ys;
   xs.add(x);
   return (String)ys.iterator().next(); // (11) run time error
}
이러한 행위를 erasure라고 합니다. <>를 제거하고, 나머지 부분에서 사용된 type variable들은 type variable의 upper bound(최상위 type)으로 변환됩니다. 대부분의 경우 Object일 것입니다. 그리고, type에 맞지 않는 곳에는 type cast(예에서는 (String)) 코드를 집어 넣습니다.
위의 예에서는 (10), (11)에서 ClassCastException이 발생할 것입니다. 프로그래머는 unchecked warning을 주의깊게 봐야 합니다.


Generic code를 Legacy code에서 사용할 경우도 본문에는 예가 있습니다. 제가 볼 땐 Legacy code를 Generic code에서 사용하는 것과 type 관련 처리 방식은 같아 보입니다. uncheck warning을 주의하면 될 것 같습니다.

generic class는 마치 static type 처럼 <>안의 내용과 관계없이 하나의 class에서 모두 처리합니다. 아래 예의 출력값은 true 입니다.

List<String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();
System.out.println(l1.getClass() == l2.getClass());

추가적으로 generic class에 대해서는 type casting 이나 instanceof와 같은 것들이 불가능하다.
(instanceof 오류)

Collection cs = new ArrayList<String>();
if (cs instanceof Collection<String>)
   ...
(type casting 오류)

Collection<String> cstr = (Collection<String>)cs;

array obejct (배열)에 대해서는 wildcard가 아닌 parameterized type이나 type variable을 적용할 수 없습니다.

List<String>[] lsa = new List<String>[10]; // (12) compile time error
Object o = lsa;
Object[] oa = (Object[])o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
oa[1] = li;
String s = lsa[1].get(0);
위의 코드는 (12)에서 compile error가 발생합니다.


wildcard를 사용하여 아래와 같이 하면 compile은 되지만 (13)에서 run time error가 발생합니다.
List<?>[] lsa = new List<?>[10];
Object o = lsa;
Object[] oa = (Object[])o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
oa[1] = li;
String s = (String)lsa[1].get(0); // (13) ClassCastException


JDK 1.5의 java.lang.Class class에는 generic이 적용되어 있습니다. 뿐만 아니라 JDK 1.5의 다른 class 들(본문에 자주 나오는 Collection 계열 등)에도 필요한 곳에는 generic이 적용되어 있습니다.

Class class의 newIntance()는 public T newIntance();와 같은 형태로 선언되어 있고, 리턴 type이 T가 됩니다. T는 Class의 선언부에 class Class<T>와 같은 type이 넘어온다.
결과로 아래와 같이 reflection 코드에서 기존보다 정확한 type을 넘겨받아서 처리가 가능하다.
public static <T> Collection<T> select(Class<T> c, String sqlStatement) {
   Collection<T> result = new ArrayList<T>();
   /* run sql query jdbc */
   for (/* iterate over jdbc results */) {
       T itme = c.newInstance();
       /* use reflection and set all of item's fields from sql results */
       result.add(item);
   }
   return result;
}


upper bounded wildcard의 반대 개념으로 lower bounded wildcard를 사용할 수 있습니다.
public static <T extends Comparable<? super T>> T max(Collection<T> coll);

wildcard capture는 이해하는데 실패했기 때문에 언급하지 않겠습니다.



java.util.Collections class의 JDK 1.4 버전의 코드는 아래와 같습니다.
public static Object max(Collection coll);
위의 코드가 JDK 1.5 에서는 아래와 같이 generic을 적용한 버전으로 바뀝니다.
public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)

"<T extends Object & Comparable<? super T>>" 부분은 multiple bounds라고 하고 "&" 기호로 각 type parameter를 구분합니다. <>안의 모든 부분을 만족하는 subtype 이라는 의미입니다.

"<T extends Object & Comparable<? super T>>"와 같이 복잡해진 이유는 JDK 1.4 에서 max()의 return type이 Object이기 때문에 호환을 위해서 입니다. JDK 1.5에서는 Ojbect를 상속하고, Comparable을 구현한 type을 return type으로 갖습니다.



본문을 대략 정리해봤는데, 영어도 짧고 해서 정확하게 정리가 되었는지 모르겠습니다.
JDK 1.5를 계속 쓰면서 숙달해야 할 것 같습니다.
Posted by [czar]
,
import java.text.*;
String str = new DecimalFormat ( "###,###.###" ).format ( 123456.789 );
//결과 123,456.789

String str = new DecimalFormat ( "###,###.###;-"###,###.###" ).format ( -123456.789 );
//결과 -123,456.789

String str = new DecimalFormat ( "###,###.##" ).format ( 123.456 );
//결과 123.45

String str = new DecimalFormat ( "###.###%" ).format ( 0.123 );
//결과 12.3%

String str = new DecimalFormat ( "000000.000" ).format ( 123.45 );
//결과 000123.450

String str = new DecimalFormat ( "$###,###.###" ).format ( 12345.67 );
//결과 $12,345.67

String str = new DecimalFormat ( "\u005a###,###.###" ).format ( 12345.67 );
//결과 엔12,345.67
Posted by [czar]
,

// 정상적으로 출력되는 한글 charset 찾기



Posted by [czar]
,
Posted by [czar]
,
<%@ page contentType="text/html; charset=euc-kr"%>
<%
   String dir = "/home/test/images";

   java.io.File f = new java.io.File(dir);
   if(f.exists()){
       String [] filelist = f.list();
       for(int i=0;i<filelist.length;i++){
           java.io.File f2 = new java.io.File(dir + "/" + filelist[i]);
        
           out.println("<a href='/images/"+f2.getName()+"'>"+f2.getName()+"</a><BR>");
       }
   }
%>


           f2.isDirectory()
               out.println("<div class=\"folder\">"+f2.getName()+"<div>");
           f2.isFile()
               out.println("<div class=\"file\">"+f2.getName()+"<div>");
Posted by [czar]
,