J2SE 5 뭐가 달라졌나? 프로그래밍

이 글은 원래 Effective Java의 저자인 Joshua Bloch 이 쓴 시에 부연 설명을 붙인 것으로, Java 5 Tiger가 기존 Java 1.4 와 달라진 점을 시로 쓴 것이다.
Yahoo Blog를 사용할 때 번역하다 만 것인데, 마저 번역한다.

원래 영어를 잘 못하는데다 시이기 때문에 번역이 매우 난해한데, 사실 의미 파악이 안돼서, 직접 코드를 실행해보고 이해한 내용을 내맘대로 써버린 부분도 많다.
그리고 원문은 베타 버전일 때 나온 것이기 때문에 정식 버전이 나온 현재와는 다른 점이 있다. 그에 관해서는 직접 코드를 테스트하여 달라진 점을 기록하였고, 몇몇 원문에 없는 내용도 추가되었다.

http://java.sun.com/j2se/1.5.0/docs/relnotes/features.html에서 더 자세하게 Java 5의 달라진 점을 알아볼 수 있으므로 참고한다.

추가 : http://www.suntraining.co.kr/jsp/certification/study/SCJP5.pdf 에도 Java 5에 관한 사항이 잘 정리되어 있다.

추가 : Dev2Dev의 Java 5의 새로운 기능

tags : java 5 1.5 tiger j2se what's new 자바 타이거 달라진점
J2SE 5 뭐가 달라졌나?

Based on Joshua Bloch's on-line talk


원문 : http://www.javacamp.org/scjp/whatsnew15.html
번역 : 손권남(kwon37xi@yahoo.co.kr, http://kwon37xi.egloos.com)

1. J2SE 5.0(타이거-호랑이) 베타버전이 2004년 초 공개됐다. 이 릴리즈는 품질, 모니터링과 관리 편의, 성능 향상과 확장성 그리고 개발과 데스크탑 용도 사용 편의라는 몇몇 핵심 주제에 포커스를 맞추고 있다.

2. Joshua Bloch 가 쓴 타이거에 관한 시가 아래에 인용되어 있다.

3. 제너릭(Generics) - 컴파일 할 때 컬렉션에 자료형을 안전하게 사용할 수 있도록 보장해주며, 짜증나는 캐스팅을 없애준다.

시 인용:
가장 짜증나는 캐스팅에게
우리는 드디어 작별을 고하네
제너릭의 불타오르는 창끝에
캐스팅은 사라져가네.

String 객체를 위해 java.util.List를 사용해서 값을 가져올 때마다 String 으로 캐스팅을 해야했다.

List names = new ArrayList();
...
String name = (String)names.get(idx);


제너릭을 이용하면, List<String>으로 선언하여 컴파일러가 대신 형을 검사하게 한다. 자료형을 안전하게 사용할 수 있고, 리스트에서 값을 가져올 때 캐스팅도 불필요 하다. 그러므로 위 코드는 다음과 같이 쓸 수 있다.

List<String> names = new ArrayList<String>();
...
String name = names.get(idx);

주의: J2SE 컴파일러는 자동으로 자료형을 검사해 줄것이다. ClassCastException은 발생하지 않을 것이고, 컬렉션의 요소가 필요할 때 캐스팅할 필요도 없다.

예제 하나 더 : 4문자 단어 없애기

현재의 코드:
static void expurgate(Collection c) {
    for (Iterator i = c.iterator(); i.hasNext(); ) {
        String s = (String) i.next();
        if(s.length() == 4)
            i.remove();
    }
}

제너릭을 이용한 코드:
static void expurgate(Collection<String> c) {
    for (Iterator<String> i = c.iterator(); i.hasNext(); )
        if (i.next().length() == 4)
            i.remove();
}

자바의 제너릭은 형을 철저하게 검사하여, 오직 지정된 형 파라미터의 메소드만을 호출 할 수 있다.
제너릭 메소드 선언의 예를 보이면:
    ....
    void f(T t) { 
       t.whatever(); 
    }

그리고 이 메소드의 형 파라미터를 Foo의 서브클래스나 서브인터페이스로 지정했다.
만약 Foo가 whtaever() 메소드를 가지고 있다면, whtever() 메소드를 사용할 수 있고, 정적으로 형 검사를 하게 되며 안전한 형이 된다.

형 파라미터 여러개 주기:
<T extends Interface1 & Interface2>

서로 다른 형 파라미터를 지정할 때 쉼표(,) 대신에 & 가 쓰였음을 주의하라.

4. 발전된 for 반복문 - 짜증나고 오류나기 쉬운 이터레이터를 없애준다.

시 인용:
이터레이터가 쓰일 때
그들은 때때로 올가미 처럼 우리를 옥죄었네
발전된 for 문이 한줄기 서광이되어
이터레이터를 궁지에 빠뜨렸네.

발전된 for 반복문("foreach"로 알려진)이 이터레이터나 인덱싱 변수 없이 컬렉션과 배열을 순회 할 수 있게 해준다.

현재의 코드:
void cancelAll(Collection c) { 
    for (Iterator i = c.iterator(); i.hasNext(); ) {
        TimerTask tt = (TimerTask) i.next();
        tt.cancel();
    }
}

J2SE 5.0의 코드:
void cancelAll(Collection c) {
    for (Object o : c)
        ((TimerTask)o).close();
}

콜론( : )은 "in."이라고 읽는다.

혹은 두가지 새로운 기능을 이용해:
void cancelAll(Collection<TimerTask> c) {
    for (TimerTask task : c)
        task.cancel();
}


5. 오토박싱(Autoboxing)/언박싱(Unboxing) - 원시 형(int 같은)과 래퍼 객체(Integer 같은)간의 짜증나는 수동 변환을 없애준다.
예를 들면,

시 인용:
컬렉션에서 int 를 빼낼 때 마다
래퍼 클래스는 우릴 슬프게 만들었네.
호랑이가 나타나면 우리는 눈물을 멈추리
우리는 이삭이 패이면 그대로 오토박싱 할 것이다.


J2SE 5.0 코드:
public class Freq {
    public static void main(String args[]) {
       Map<String, Integer> m = new TreeMap<String, Integer>();
       for (String word : args)
           m.put(word, m.get(word) + 1);
       System.out.println(m);
    }
}


현재의 코드:
public class Freq {
    private static final Integer ONE = new Integer(1);

    public static void main(String args[]) {
        Map m = new TreeMap();

        for (int i=0; i<args.length; i++) {
            Integer freq = (Integer) m.get(args[i]);
            m.put(args[i], (freq==null ? ONE :
                  new Integer(freq.intValue() + 1)));
        }
        System.out.println(m);
    }
}

 * Boxing : 객체지향 언어에서 원시 형(primitive type, int, long 등)을 래퍼 객체(Integer, Long 등)에 넣어서 객체처럼 사용할 수 있게 하는 것을 의미한다. Java의 경우 원시형은 객체가 아니므로 객체만 저장 가능한 List 등에 원시형을 넣을 때 래퍼 객체에 원시형을 넣고(Boxing 하고) 그 후에 List에 넣어야 한다. Java 5 이전에는 프로그래머가 직접 Boxing을 해야했다.
 * Autoboxing : Autoboxing은 부가적인 코드 없이 원시형을 자동으로 객체로 취급하는 것을 의미한다.
 * Unboxing : Unboxing은 부가적인 코드 없이 래퍼 객체를 자동으로 원시형으로 취급하는 것을 의미한다.
   from Wikipedia

6. 형이 안전한(Typesafe) enum - 이것은 말 많고 에러 나기 쉬운 점들이 없이 Typesafe Enum 의 잘 잘려진 장점들을 제공해 준다.

주의 : enum 은 Java 5에새 새로 생긴 키워드이므로, 옛날 자바 코드에서 enum을 형의 이름(변수/클래스/메소드 이름 등)으로 사용했다면, -source 1.5 를 사용하기 위해 그것들을 모두 수정해야 한다.

시 인용:
정수 enum 은 곧 사라지리
너무 오랫동안 우리를 괴롭혔던 적과 함께.
형이 안전한 enum의 강력한 힘은
우리의 적을 무력화 시키네.

* Coin.java --------------------
public enum Coin {
    penny(1), nickel(5), dime(10), quarter(25);

    Coin(int value) { this.value = value; }

    private final int value;

    public int value() { return value; }
}

* CoinTest.java --------------------
public class CoinTest {
    public static void main(String[] args) {
        for (Coin c : Coin.values()) // 바뀐 부분
            System.out.println(c + ":    "
                  + c.value() +"?  " + color(c));
    }
    private enum CoinColor { copper, nickel, silver }
    private static CoinColor color(Coin c) {
        switch(c) {
          case penny:   return CoinColor.copper;
          case nickel:  return CoinColor.nickel;
          case dime:
          case quarter: return CoinColor.silver;
          default: throw new AssertionError("Unknown coin: " + c);
        }
    }
}

상수 선언이 동전을 대표하는 숫자를 넘겨주면서 어떻게 생성자를 호출하는지 보라. 그리고 어떻게 private 필드에 public 접근자로 값을 저장하는지 보라. 색깔을 위한 또 다른 enum을 어떻게 생성했는지 보라. enum 상수를 어떻게 switch 구문에 사용하는지 보라. 이것은 당신이 enum 클래스에 "메소드를 추가"하고자 할 때 매우 유용하다. 하지만 어떤 이유로든 클래스를 수정할 수는 없다.

사실 새로운 enum은 다른 언어의 구조체(struct)에 불과하다. enum에 메소드와 필드를 추가하지 않는다면 그것은 형이 안전한 enum 상수와 같다. 만약 메소드와 필드를 추가하면, 완전한 객체가 된다.
 * 추가
  - Java 5의 enum 형에는 메소드와 필드를 추가할 수 있다. 다른 언어의 enum 형과 다른 점이다.
  - 각 enum 타입은 static values() 메소드를 가지고 있다. 이 메소드는 enum의 모든 값을 배열로 리턴한다.
  - 원문에서는 Coin.VALUES로 Coin의 상수 값들을 가져왔지만, 실제론 작동하지 않는다. values() 메소드를 사용해야 한다.
  - 원문에서는 switch의 case 문에서 Coin.penny 와 같은 식으로 값을 지정했지만 실제로는 penny 로만 지정하게 바뀌었다. switch/case에서 case 문에 사용되는 enum 형에만 해당된다. 리턴값인 CoinColor.copper는 그대로이다.

7. Static import - Static import 는 Constant Interface 안티패턴의 단점을 피해, 클래스이름없이 정적 멤버를 사용할 수 있게 해준다.

예를 들면:

시 인용:
그리고 상수 인터페이스로부터
우리는 더이상 불명예를 물려받지 않으리라.
static import를 통해
우리의 기쁨이 무한해 지리라.

public interface Promotion {
  public static final double MANAGER_RATE   = 0.59;
  public static final double SUPERVISER_RATE   = 0.35;
}

Promotion 인터페이스를 사용하려할 때, 지금까지는 인터페이스를 구현(implement)해야 했다. 하지만 J2SE 5.0 에서는 다음과 같이 import 해도 된다.

import static whatever.sub.Promotion.*;

class Test {
    public static void main(String[] args) {
        double extra_salary = MANAGER_RATE * current_salary;
        ...
    }
}

주의: Promotion은 클래스이어도 된다. 상수 클래스 디자인에 유용하다.
주의: static 메소드도 같은 방식으로 사용할 수 있다.
주의: 원문에서는 "import static whatever.sub.Promotion"했지만 실제로는 "import static whatever.sub.Promotion.*"해야 한다.
주의: static import는 남용해서는 안된다. 남용하면 코드의 가독성을 떨어뜨린다. 되도록 static import 할 때 멤버명까지 지정하는 것이 좋다. 멤버명은 상수 멤버나 public static 메소드명도 된다.

8. Metadata(Annotation) - 어노테이션은 소스코드내에 작성된 어노테이션을 통해 자동으로 코드를 생성해 주어, 반복적인 공통 코드 작성을 피하게 해준다. 어노테이션은 프로그래머가 뭔가가 작동해야 한다고 말하는 그 지점에서 "선언적 프로그래밍"이 가능하게 만들고, 도구는 그 작업을 하는 코드를 생성해낸다.

시 인용:
그리고 고귀하신 메타데이타(Annotation)에 대해서는
나중으로 찬양을 미뤄야겠다.
그의 쓰임이 너무도 다양하여
제대로 찬미하다간, 버스를 놓칠 판이다.

public class Test { 
    @Somewhere public void someMethod() {
        ...
    }
}

@Somewhere 어노테이션에 의해 코드가 생성될 것이다.

이것은 "프로그램 어노테이션"이라고 불린다.

주의 : 메타데이타를 위해 새로운 키워드가 추가되지는 않았다. 대신 이미 쓰이는 기호를 다시 사용했다. 어노테이션 형을 정의하려면:
 public @interface Foo { ... } 

저기 "@interface"가 "어노테이션 형(annotation-type)" (눈치챘나요? a-t -> at -> @)이다.. 또한 이것은 javadoc 태그 처럼 보이기도 한다.

어노테이션 사용:
@Copyright("Acme Inc") public static final int baz(...) {
    ...
}


9. Java 변수 인자들이 객체의 배열이다. 컴파일러는 인자 메소드에 전달된 목록에 기반하여 배열로 합친다.
public void aMethod(String... args) {
    for(String s : args) {
        System.out.println(s);
    }
}

aMethod 는 다음과 같은 형태로 호출된다 :

aMethod("here","we","go"); //3 argument list
aMethod("to","be","or","not","to","be"); //6 argument list

컴파일러는 인자 목록을 자동으로 다음과 같이 만든다 :
aMethod(new String[] {"here","we","go"});
aMethod(new String[] {"to","be","or","not","to","be"});

참조 : 이 varargs 기능을 이용한 메소드들이 몇몇 추가 되었다. 다음을 참조해보라.
 - java.io.PrintWriter.printf()
 - java.util.Formatter.format()

10. 리플렉션 API가 enum, 제너릭, 어노테이션을 다룰 수 있도록 확장되었다.
11. J2SE 5.0은 바뀐 핵식 XML 플랫폼을 도입하고 있다. XML 1.1, SAX 2.0 그리고 DOM Level 3.
12. 32-bit 대행 문자지원이 조심스럽게 추가되어, 기존 Java "char"형은 여전히 16bit로 남아있다.
13. java.lang.StringBuilder가 추가되었다. 이것은 java.lang.StringBuffer의 비동기(unsynchronized)버전이다. StringBuffer가 동기화되어 있기 때문에 문자열 연결이 약간 느리다. 컴파일러는 StringBuilder를 문자열 합치기 연산에 사용할 것이고, 이로인해 몇몇 어플리케이션에서는 상당한 성능향상을 보일 것이다. (다시말해, 쓰레드에 안전한 연산에서는 StringBuffer보다 StringBuilder를 사용하는 것이 좋다!)
14. XP 룩앤필과 리눅스 GTK 2.0 룩앤필이 추가되었다. 기본 룩앤필(메탈)도 개선되었다.
15. 프로그램 시작 속도가 빨라졌다.
16. X.class는 "Class" 형이다.
17. JTable과 기타 클래스의 기능을 사용하기가 쉬워졌다.
18. java.util.concurrent(병행 프로그래밍 API)같은 많은 새로운 API들이 추가되었다.
19. weak reference와 thread local 같은 모든 래퍼(wrapper) 클래스들은 제너릭을 사용할 수 있도록 개선되었다. List 같은 열거형 제너릭을 리턴하는 몇몇 API들도 마찬가지다.
20. Tuple 이라 불리우는 여러 리턴형을 다루는 방법이 더 미래의 Java 버전에 도입될 예정이다.
21. "const" 키워드가 J2SE 5.0이나 그 이후 버전에서 사라질 것이다. 대신 "final"을 사용하라.
22. java.util.Date는 그대로이다. 약간의 달라진점이라면, Comparable 대신 Comparable를 구현(implement)했다는 점이다. Date 클래스는 값이 변하는 문제를 앉고 있다. 언제 바뀔까? 나도 모르지 뭐.
23. J2SE 5.0에는 J++와 C#에 있는 delegates 기능이 포함되지 않았다.
24. J2SE 5.0에는 연산자 오버로딩 기능이 포함되지 않았다.
25. 더 많은 달라진 점들이 있을 것이다. 귀를 기울이고 있으라.

26. JVM GC/Memory/Thread 모니터링 기능.
다음의 옵션을 주어서 어플리케이션을 실행한 뒤에,
-Dcom.sun.management.jmxremote

다음 명령으로 JCONSOLE을 실행해서 JVM을 모니터링할 수 있다.
$JAVA_HOME/bin/jconsole


Joshua Bloch의 시

Tiger, Tiger Burning Bright
Like a geek who works all night
What new-fangled bit or byte
Could ease the hacker's weary plight?

To the most despised cast
We'll bid a fond farewell at last
With generics' burning spear
The need for cast will disappear

While Iterators have their uses
They sometimes strangle us like nooses
With enhanced-for's deadly ray
Iterator's kept at bay

When from collections ints are drawn
Wrapper classes make us mourn
When Tiger comes, we'll shed no tears
We'll autobox them in the ears

The int-enum will soon be gone
like a foe we've known too long
With typesafe-enum's mighty power
Our foe will bother us no more

And from the constant interface
we shall inherit no disgrace
With static import at our side,
our joy will be unqualified

And as for noble metadata
I'll have to sing its praises later
Its uses are so numerous
To give their due, I'd miss the bus


핑백

  • Heart&#8217;s CodeList &raquo; 흥미로운 문제들&#8230; 080212 2008-05-22 23:26:30 #

    ... StringBuilder 클래스(StringBuffer 클래스의 비동기화 버전) 를 사용하여 합성한다는 점을 새로 배웠고, 게다가 J2SE 5.0 변경사항에 대한 번역/설명이 담긴 링크도 얻을 수 있었다. 아무튼 흥미로운 문제를 제공해 주신 써니님께 감사를&#8230; ps. 위의 문제의 답들이 써니님 블로그에 올라오는 중이다. 아직 다 올라오지 않아서 링크 ... more

덧글

  • 넷개구락지 2007/01/17 10:32 # 삭제

    좋은 글 감사드립니다. by 넷개구락지
  • Heart 2008/02/12 16:54 # 삭제

    번역과 추가 내용 정말 잘 보았습니다. :)
  • 세지니 2008/06/18 17:54 # 삭제

    좋은 내용 감사합니다. 궁금했던 내용입니다.
※ 로그인 사용자만 덧글을 남길 수 있습니다.