2007년 2월 26일 월요일

Java Annotation 해석

Language Contents(언어 관련 주제들)

Annotations(덧붙이는 말)
--------------------------------------------------------------------------------

많은 API들은 상당한 양의 반복 사용되는 코드를 요구합니다. 예를들어, JAX-RPC 웹 서비스를 작성하기 위해서, 당신은 interface와 구현부분을 쌍으로 제공해야만 합니다. 이러한 반복 사용되는 코드는 만약 그 프로그램이 어떤 메서드들이 원격으로 접근이 되는지 [덧붙이는 말]과 함께 "꾸며졌다면" 도구에 의해서 자동으로 생성되어질 수 있습니다.

다른 API들은 프로그램과 동시에 유지해져야 하는 "부가적인 파일들"을 요구합니다. 예를 들어, JavaBeans는 BeanInfo 클래스를 bean과 동시에 유지해져야 하고, Enterprise JavaBeans(EJB)는 배포 설명자를 요구하죠. [덧붙이는 말]은 만약에 이런 부가적인 파일들 안에 있는 정보들이 프로그램 자체 내에 내장하고 있는 [덧붙이는 말]과 같이 유지된다면 더욱 편리하고 더 에러를 적게 내게 됩니다.

Java platform은 항상 다양한 괴상한 [덧붙이는 말] 기술들을 가져왔습니다. 예를 들어서, transient 표시자는 그 field는 serialization 하위 시스템에 의해서 무시되어져야만 하는 field를 나타내는데 쓰이는 이상한 [덧붙이는 말]이고, @deprecated javadoc 태그는 method가 더이상 사용되지 않아야 한다는 것을 나타내는 이상한 [덧붙이는 말]입니다. 5.0을 배포판에 따라, platform은 일반적인 목적의 [덧붙이는 말](또한 metadata로 알려진) 기능을 가지게 되었고, 이것은 당신이 정의하고 사용할 수 있는 개개인의 [덧붙인는 형태]를 허용해줍니다. 그 기능은 [덧붙이는 형태에 대한 정의], [덧붙여지는 정의]에 대한 문법과 [덧붙여지는 말들을 읽기 위한 API], [덧붙이는 말을 표시하기 위한 클래스 파일], 그리고 [덧붙여지는 말을 처리하는 도구]를 포함합니다.

[덧붙이는 말]은 직접적으로 프로그램의 의미에 영향을 주지 않으나, 도구들과 라이브러리들에의해 여겨지는 바가 다르도록 영향을 끼칩니다. 이 말은 실행 중인 프로그램의 의미가 번갈아서 영향을 받을 수 있다는 뜻입니다. [덧붙이는 말]은 소스 파일, 클래스 파일, 또는 실행시간에 Reflection(실행 시간의 클래스 명이나 메서드 명 같은 것을 얻어오는 자바의 수단)으로 부터 읽어들여질 수 있습니다.

[덧붙이는 말은] javadoc 태그들을 보조합니다. 일반적으로, 만약 표시가 영향을 미치도록 의도되었거나 문서화를 하도록 의도되었다면, 그것은 아마도 javadoc 태그가 되어져야만 합니다; 아니면, 그것은 하나의 [덧붙이는 말]이 되어야 합니다.

전형적인 응용 프로그램 개발자들은 절대로 [덧붙이는 말 형태]를 정의하지 않아야 할 것이지만, 그렇게 하는 건 그다지 어렵지 않습니다. [덧붙이는 말 형태]는 일반적인 interface 정의와 유사합니다. 하나의 at-표시(@) 를 interface 예약어 앞에 적으면 됩니다. 각각의 method 선언은 하나의 [덧붙이는 말 형태]의 element를 정의 합니다. Method 선언은 아무런 파라메터도 가져서는 안되고 throws 구문을 가져도 안됩니다. 반환 형태는 기본 형태인 String, Class, enums, [덧붙이는 말], 그리고 앞에 나열한 형태들의 배열로 강제 됩니다. Method들은 기본 값을 가질 수 있습니다. 여기에 [덧붙이는 말 형태] 선언에 대한 하나의 예가 있습니다.

/**
 * 확장을 위한 요청(RFE)을 설명합니다. 이것은 [덧붙이는 말]이 붙어진
 * API element가 존재하도록 합니다.

 */

public @interface RequestForEnhancement {
    int    id();
    String synopsis();
    String engineer() default "[unassigned]";
    String date();    default "[unimplemented]";
}

한번 [덧붙이는 말 형태]가 정의 되면, 당신은 [덧붙이는 말 선언]을 사용할 수 있습니다. 하나의 [덧붙이는 말]은 특별한 종류의 표시자 이고, 다른 표시자들 이 사용될 수 있는(public, static, 또는 final) 아무 위치에서나 사용되어질 수 있습니다. [덧붙이는 말]들은 하나의 at 표시(@) 다음에 [덧붙이는 말 형태]와 element-value 쌍의 리스트들을 중괄호로 묶여진 것을 포함하고 있습니다. value들은 확실히 컴파일 시점의 상수가 될 것입니다. 여기에 위에서 선언한 [덧붙이는 말 선언]에 대응하는, [덧붙이는 말]을 가진 하나의 method 선언이 있습니다:

@RequestForEnhancement(
    id       = 2868724,
    synopsis = "Enable time-travel",
    engineer = "Mr. Peabody",
    date     = "4/1/3007"
)

public static void travelThroughTime(Date destination) { ... }
element가 없는 하나의 [덧붙이는 말 형태]는 하나의 구두점 [덧붙이는 말 형태]로 표시됩니다, 예를 들어:

/**
 * [덧붙여진 API 요소]의 명시는 변화를 예고하고, 변경되기 쉽다는 것을

 * 나타냅니다.
 */

public @interface Preliminary { }

내용들을 구두점 [덧붙이는 말]로 생략하는 게 허용됩니다, 아래처럼 말이죠:

@Preliminary public class TimeTravel { ... }

[덧붙이는 말]에 단일 element만 있는 경우, element는 반드시 value로 이름지어져야 합니다, 아래처럼 말이죠:

/**
 * [덧붙여진 API element]와 관련있는 저작권 알림.
 */
public @interface Copyright {
    String value();
}

그것은 element 명과 등호 표시(=)를 하나의 단일-element [덧붙이는 말]을 생략할 수 있도록 허용합니다. 이 [덧붙이는 말]의 element 명이 value일 경우엔 말이죠, 아래와 같이 말이죠:

@Copyright("2006 CONI 임은천")
public class OscillationOverthruster { ... }

그것 모두를 묶기 위해서, 우리는 하나의 단순한 [덧붙이는 말]에 기반을 test framework를 작성하겠습니다. 첫째로, 우리는 하나의 method가 test method라는 것을 나타내고, test 하는 도구를 통해서 실행되어져야 한다는 것을 암시하기 위해서 하나의 표시자 [덧붙이는 말 형태]가 필요합니다:

import java.lang.annotation.*;

/**
 * [덧붙이는 말]이 붙은 method는 test method를 암시합니다.
 * 이 [덧붙이는 말]은 parameter가 없는 static method 여야 합니다.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test { }

[덧붙이는 말 형태] 선언은 스스로를 [덧붙이도록] 한다는 것에 주의 하세요. 그런 [덧붙이는 말]을 meta-annotations(부연 : 덧붙이는 말을 설명하는 덧붙이는 말)이라고 불립니다. 첫번째는(@Retention(RetentionPolicy.RUNTIME)) 이 형태의 [덧붙이는 말]로 선언된 것은 VM에 의해서 유지되므로 실행-시간에 reflection을 통해서 읽어질 수 있다는 것을 암시합니다. 두번째 것은 (@Target(ElementType.METHOD))  이 [덧붙이는 말] 형태가 오직 method 선언을 [덧붙이는 말]을 적용하는데만 쓰일 수 있다는 것을 암시합니다.

여기에 프로그램의 몇몇 method가 위의 interface로 [덧붙이는 말]이 적용된 예제 프로그램이 있습니다:

public class Foo {
    @Test public static void m1() { }
    public static void m2() { }
    @Test public static void m3() {
        throw new RuntimeException("Boom");
    }
    public static void m4() { }
    @Test public static void m5() { }
    public static void m6() { }
    @Test public static void m7() {
        throw new RuntimeException("Crash");
    }
    public static void m8() { }
}

여기 부터는 test 하는 도구의 코드 입니다:

import java.lang.reflect.*;

public class RunTests {
   public static void main(String[] args) throws Exception {
      int passed = 0, failed = 0;
      for (Method m : Class.forName(args[0]).getMethods()) {
         if (m.isAnnotationPresent(Test.class)) {
            try {
               m.invoke(null);
               passed++;
            } catch (Throwable ex) {
               System.out.printf("Test %s failed: %s %n", m, ex.getCause());
               failed++;
            }
         }
      }
      System.out.printf("Passed: %d, Failed %d%n", passed, failed);
   }
}

도구는 class 명을 command line의 매개 변수처럼 쓰고 명시된 class의 모든 method를 하나씩 지나가면서 Test [덧붙이는 말](위에 정의된)이 적용된 각각의 method를 실행해 보려고 시도합니다. 하나의 method가 하나의 Test [덧붙이는 말]을 가지고 있는지 Reflection을 통해 조사하는 문장은 연두색으로 색칠되어 있습니다. 만약 하나의 test method 실행이 exception을 throws 한다면, test는 실패한 것으로 여겨질 것이고, 실패 보고서가 출력됩니다. 끝으로, test 중에 성공하고 실패한 갯수를 보여주는 요약 내용이 출력됩니다. 여기에 당신이 test 하는 도구를 Foo 프로그램(위의 프로그램)을 실행했을 때 어떻게 보이는지 있습니다:
$ java RunTests Foo
Test public static void Foo.m3() failed: java.lang.RuntimeException: Boom
Test public static void Foo.m7() failed: java.lang.RuntimeException: Crash
Passed: 2, Failed 2

이 test하는 도구가 확실히 하찮은 것일지라도, 그것은 [덧붙이는 말]의 power를 보여주고 그것의 제한성을 능가하기 위해서 쉽게 확장될 수 있다는 것을 보여줍니다.
--------------------------------------------------------------------------------
Copyright © 2004 Sun Microsystems, Inc. All Rights Reserved.
Please send us comments and suggestions

저작권  © 2004 Sun Microsystmes, Inc. 모든 권리가 있습니다.
저희에게 조언과 제안을 부탁드립니다.
Java Software

-------------------------------- 원문http://java.sun.com/j2se/1.5.0/docs/guide/language/annotations.html

댓글 없음:

댓글 쓰기