끈기JK 2023. 2. 28. 09:23

com.eomcs.annotation

 

예제 소스 정리

 

 

annotation

 

 

com.eomcs.annotation.ex1

 

 

애노테이션 사용
package com.eomcs.annotation.ex1;


//=> 클래스, 필드, 메서드, 로컬 변수 선언에 붙이는 특별한 주석이다.
//=> 다른 주석과 달리 컴파일이나 실행할 때 추출할 수 있다.
//=> 애노테이션 문법이 도입되기 전에 
//   일반 주석에 특별한 문법을 포함시켜 사용했던 doclet 이라는 기술이 있었다.
//=> 일반 주석과 달리 '프로퍼티명=값' 형태로 값을 다룰 수 있다.
//=> 사용법
//   - 애노테이션 정의 또는 기존에 정의된 애노테이션 사용
//   - 클래스나 인터페이스에 적용
//=> .class 파일에 포함된 애노테이션을 확인하라!
//

@MyAnnotation // 클래스 선언에 붙일 수 있다.
public class Exam0110 {

  @MyAnnotation // 필드에 붙일 수 있다.
  static int a;

  @MyAnnotation int b; // 필드 선언 바로 앞에 둘 수 있다.

  @MyAnnotation // 메서드 선언에 붙일 수 있다.
  void m1(
      @MyAnnotation 
      int p1, // 파라미터(로컬변수)에 붙일 수 있다. 

      @MyAnnotation String p2
      ) {

    @MyAnnotation int local; // 로컬변수 선언에 붙일 수 있다.

    //@MyAnnotation System.out.println("okok"); // 그러나 다른 일반 문장에는 붙일 수 없다.

    //@MyAnnotation // 다른 일반 문장에는 붙일 수 없다.
    for (int i = 0; i < 100; i++) {
      @MyAnnotation int a; // 로컬 변수 선언에 붙일 수 있다.
    }
  }

  @MyAnnotation  // static, non-static 상관없이 메서드 선언에 붙일 수 있다.
  static void m2() {

  }
}

 

// 애노테이션 정의
package com.eomcs.annotation.ex1;

public @interface MyAnnotation {

}

//한 줄 주석 
//- 소스 코드의 설명을 붙일 때 사용
//- 컴파일 할 때 .class 파일에 포함되지 않음

/* 
여러 줄 주석
- 소스 코드에 여러 줄의 설명을 붙일 때 사용 
- 컴파일 할 때 .class 파일에 포함되지 않음
 */

/**
javadoc 주석
- javadoc.exe 를 통해 HTML API 문서를 생성할 때 사용됨.
- 컴파일 할 때 .class 파일에 포함되지 않음
 */


//애노테이션
//- 클래스 파일(.class)에 남길 수 있는 주석
//- 형식을 갖춘 주석이다.
//- 형식?
//    @애노테이션이름(프로퍼티=값, 프로퍼티=값) 
//- 활용
//  1) 소스 코드에서 주석을 읽어 다른 소스 파일을 생성할 때 사용
//  2) 컴파일할 때 주석을 추출하여 사용(컴파일러가 사용하는 내장 애노테이션)
//  2) 실행 중에 주석을 추출하여 사용

 

 

애노테이션 프로퍼티 사용
package com.eomcs.annotation.ex1;

@MyAnnotation2(value="okok")
public class Exam0120 {

  @MyAnnotation2(value="okok")
  int a;

  @MyAnnotation2(value="okok")
  void m() {}
}

 

// 애노테이션 프로퍼티
package com.eomcs.annotation.ex1;

public @interface MyAnnotation2 {

  // 프로퍼티를 정의할 수 있다.
  // => 인터페이스에서 메서드를 정의하는 것과 유사하다.
  // => 메서드 이름은 프로퍼티(변수)명처럼 작성한다.
  //    즉 일반적인 메서드는 보통 동사로 이름을 시작하지만,
  //    애노테이션은 명사(명사구)로 이름을 짓는다.
  // => 값을 꺼낼 때, 메서드 호출로 꺼낸다.
  // => 애노케이션에서 값을 설정할 때는
  //    다음과 같이 변수 형태를 사용한다.
  //       value="hello"
  //    그래서 프로퍼티의 이름을 변수 형태로 짓는 것이다.
  //    즉, getValue 가 아니라 value라고 한다.
  String value(); // 애노테이션의 기본 프로퍼티이다.

}

 

 

com.eomcs.annotation.ex2

 

 

애노테이션 유지 정책 확인
package com.eomcs.annotation.ex2;

//애노테이션 유지 범위 
//=> CLASS
//   - .class 파일까지는 유지된다. 
//   - 그러나 runtime에는 메모리에 로딩되지 않는다.
//   - 애노테이션을 정의할 때 유지 범위를 지정하지 않으면 기본이 CLASS 이다.
//=> SOURCE
//   - 컴파일 할 때 제거된다. 
//   - .class 파일에 포함되지 않는다.
//   - 보통 소스 파일에서 애노테이션 값을 꺼내 다른 파일을 자동 생성하는 도구를 만들 때 많이 사용한다.
//=> RUNTIME
//   - .class 파일까지 유지되고, runtime에 메모리에 로딩된다.
//   - 실행 중에 애노테이션을 참조해야 할 경우에 많이 사용한다.
//

public class Exam0110 {

  public static void main(String[] args) {
    // MyClass.class 파일을 편집기로 열어서 확인해보라!

    // 클래스 정보 객체로부터 애노테이션 정보 추출
    Class<?> clazz = MyClass.class;

    // => 유지정책 : CLASS
    MyAnnotation obj = clazz.getAnnotation(MyAnnotation.class);
    if (obj == null) {
      System.out.println("MyAnnotation을 추출할 수 없습니다!");
    } else {
      // 값을 꺼낼 때는 메서드 호출하듯이 꺼내면 된다.
      System.out.println("MyAnnotation.value=" + obj.value());
    }

    // => 유지정책 : SOURCE
    MyAnnotation2 obj2 = clazz.getAnnotation(MyAnnotation2.class);
    if (obj2 == null) {
      System.out.println("MyAnnotation2를 추출할 수 없습니다!");
    } else {
      System.out.println("MyAnnotation2.value=" + obj2.value());
    }

    // => 유지정책 : RUNTIME
    MyAnnotation3 obj3 = clazz.getAnnotation(MyAnnotation3.class);
    if (obj3 == null) {
      System.out.println("MyAnnotation3를 추출할 수 없습니다!");
    } else {
      System.out.println("MyAnnotation3.value=" + obj3.value());
    }

  }
}

 

// 애노테이션 사용
// => @애노테이션이름(프로퍼티명=값, 프로퍼티명=값, ...)
//
package com.eomcs.annotation.ex2;

@MyAnnotation(value="값1") // 유지정책 => CLASS 
@MyAnnotation2(value="값2") // 유지정책 => SOURCE 
@MyAnnotation3(value="값3") // 유지정책 => RUNTIME 
public class MyClass {
}

// 컴파일 한 후 .class 파일을 확인해 보라!

 

// 애노테이션 유지 정책
package com.eomcs.annotation.ex2;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

// 애노테이션의 유지 정책을 지정하지 않으면 기본이 CLASS이다.
// => 컴파일 할 때 .class 파일에 포함된다.
// => 단, 실행할 때 메모리에 로딩되지 않기 때문에 리플랙션 API로 추출할 수 없다.
@Retention(RetentionPolicy.CLASS)
public @interface MyAnnotation {
  String value();
}

//애노테이션 유지 범위
//1) SOURCE :  소스 파일에만 남긴다. *.class 파일에 포함 안됨. 즉 컴파일할 때 제거된다.
//2) CLASS (기본) : .class 파일에 포함. 실행할 때 로딩 안됨.
//3) RUNTIME : .class 파일에 포함. 실행할 때도 메모리에 로딩됨. 실행 시에 추출할 수 있다.

 

// 애노테이션 유지 정책
package com.eomcs.annotation.ex2;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

// 애노테이션 유지 정책을 SOURCE라고 지정하면
// => 해당 애노테이션은 컴파일할 때 제거된다.
@Retention(RetentionPolicy.SOURCE)
public @interface MyAnnotation2 {
  String value();
}

 

// 애노테이션 유지 정책
package com.eomcs.annotation.ex2;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

// 애노테이션 유지 정책을 RUNTIME라고 지정하면
// => 해당 애노테이션은 .class 파일에 포함된다.
// => 실행할 때 메모리에 로딩되기 때문에 리플랙션 API로 추출할 수 있다.
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation3 {
  String value();
}

 

 

com.eomcs.annotation.ex3

 

 

  애노테이션 프로퍼티 값 지정하기
package com.eomcs.annotation.ex3;

//@MyAnnotation // 필수 프로퍼티 값을 지정하지 않으면 컴파일 오류!
@MyAnnotation(value="값") // OK!

@MyAnnotation2 // 애노테이션의 프로퍼티 값을 지정하지 않으면 default 값이 사용된다.
//@MyAnnotation2(value="물론 이렇게 프로퍼티 값을 지정해도 된다.")

public class MyClass {
}

 

// 애노테이션 프로퍼티 - 필수 프로퍼티
package com.eomcs.annotation.ex3;

public @interface MyAnnotation {
  String value(); 
  // default 값을 지정하지 않으면 필수 프로퍼티가 된다.
  // 즉 애노테이션을 사용할 때 반드시 값을 지정해야 한다.
}

 

// 애노테이션 프로퍼티 - 선택 프로퍼티
package com.eomcs.annotation.ex3;

public @interface MyAnnotation2 {
  String value() default "홍길동";
  // default 값이 있으면,
  // 애노테이션을 사용할 때 값을 지정하지 않아도 된다.
}

 

 

com.eomcs.annotation.ex4

 

 

애노테이션 프로퍼티 값 지정하기 - 프로퍼티 이름 생략
package com.eomcs.annotation.ex4;

//@MyAnnotation // 오류! 기본 값이 설정되어 있지 않기 때문에 반드시 프로퍼터 값을 지정해야 한다.
//@MyAnnotation(value="홍길동") // OK!
@MyAnnotation("홍길동") // OK! 'value' 라는 이름을 가진 프로퍼티는 이름 생략 가능!
public class MyClass {
}
// 애노테이션 프로퍼티 - value 프로퍼티
package com.eomcs.annotation.ex4;

public @interface MyAnnotation {
  String value(); // 필수 프로퍼티 
}

  

package com.eomcs.annotation.ex4;

//@MyAnnotation2 // tel 프로퍼티는 필수이다. 반드시 값을 지정해야 한다.
//@MyAnnotation2(tel = "222-2222") // OK!
//@MyAnnotation2("222-2222") // 프로퍼티 명이 'value'가 아닌 경우에는 이름 생략 불가!
public class MyClass2 {
}
// 애노테이션 프로퍼티 - 일반 프로퍼티
package com.eomcs.annotation.ex4;

public @interface MyAnnotation2 {
  String tel(); // 필수 프로퍼티
}

 

package com.eomcs.annotation.ex4;

//@MyAnnotation3(value = "홍길동", tel = "222-2222") // OK!

@MyAnnotation3(tel = "222-2222", value = "홍길동") // OK!
// => 프로퍼티 값을 설정할 때 순서는 상관없다.

//@MyAnnotation3("홍길동",tel="222-2222") // 오류!
// value 외 다른 프로퍼티 값도 지정할 경우,
// value 이름 생략 불가!
// value 값만 지정할 때 생략 가능!
public class MyClass3 {
}
// 애노테이션 프로퍼티 - 프로퍼티 생략
package com.eomcs.annotation.ex4;

public @interface MyAnnotation3 {
  String value(); // 필수 프로퍼티
  String tel(); // 필수 프로퍼티
}

 

 

com.eomcs.annotation.ex5

 

 

애노테이션 프로퍼티 값 추출
package com.eomcs.annotation.ex5;

public class Exam01 {

  public static void main(String[] args) {
    Class<?> clazz = MyClass.class;
    MyAnnotation obj = clazz.getAnnotation(MyAnnotation.class);

    System.out.println(obj.v1());
    System.out.println(obj.v2());
    System.out.println(obj.v3());

  }
}
// 애노테이션 프로퍼티 타입
package com.eomcs.annotation.ex5;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String v1() default "가나다";
    int v2() default 100;
    float v3() default 3.14f;
}
// 애노테이션 프로퍼티 타입
package com.eomcs.annotation.ex5;

// 애노테이션의 모든 프로퍼티에 기본 값이 설정되어 있다면 
// 다음과 같이 프로퍼티 설정을 생략해도 된다.
// 프로퍼티 값 설정을 생략하면,
// 애노테이션을 정의할 때 지정한 값이 기본으로 적용된다.
@MyAnnotation
public class MyClass {
}

 

 

애노테이션 프로퍼티 값 추출 - 배열 값 추출
package com.eomcs.annotation.ex5;

public class Exam02 {

  public static void main(String[] args) {
    Class<?> clazz = MyClass2.class;
    MyAnnotation2 obj = clazz.getAnnotation(MyAnnotation2.class);

    printValues(obj.v1());
    System.out.println("----------------------");

    printValues(obj.v2());
    System.out.println("----------------------");

    printValues(obj.v3());

  }

  static void printValues(String[] values) {
    for (String value : values) {
      System.out.print(value + ",");
    }
    System.out.println();
  }

  static void printValues(int[] values) {
    for (int value : values) {
      System.out.print(value + ",");
    }
    System.out.println();
  }

  static void printValues(float[] values) {
    for (float value : values) {
      System.out.print(value + ",");
    }
    System.out.println();
  }
}
// 애노테이션 프로퍼티 타입 - 배열
package com.eomcs.annotation.ex5;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation2 {
  // 배열 프로퍼티의 기본 값을 지정할 때 중괄호를 사용한다.
  String[] v1() default {"가나다","라마바"};
  int[] v2() default {100, 200, 300};
  float[] v3() default {3.14f, 4.14f, 5.14f, 6.14f};
}
// 애노테이션 프로퍼티 타입
package com.eomcs.annotation.ex5;

@MyAnnotation2
public class MyClass2 {
}

 

 

애노테이션 프로퍼티 값 추출 - 배열 값 추출
package com.eomcs.annotation.ex5;

public class Exam03 {

  public static void main(String[] args) {
    Class<?> clazz = MyClass3.class;
    MyAnnotation3 obj = clazz.getAnnotation(MyAnnotation3.class);

    printValues(obj.v1());
    System.out.println("----------------------");

    printValues(obj.v2());
    System.out.println("----------------------");

    printValues(obj.v3());
  }

  static void printValues(String[] values) {
    for (String value : values) {
      System.out.print(value + ",");
    }
    System.out.println();
  }

  static void printValues(int[] values) {
    for (int value : values) {
      System.out.print(value + ",");
    }
    System.out.println();
  }

  static void printValues(float[] values) {
    for (float value : values) {
      System.out.print(value + ",");
    }
    System.out.println();
  }
}
// 애노테이션 프로퍼티 타입 - 배열
package com.eomcs.annotation.ex5;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation3 {
  //  String[] v1() default {"가나다"};
  //  int[] v2() default {100};
  //  float[] v3() default {3.14f};

  // 배열 값이 한 개일 경우 중괄호를 생략할 수 있다.
  String[] v1() default "가나다";
  int[] v2() default 100;
  float[] v3() default 3.14f;
}
// 애노테이션 프로퍼티 타입
package com.eomcs.annotation.ex5;

@MyAnnotation3
public class MyClass3 {
}

 

 

애노테이션 프로퍼티 값 추출 - 배열 값 추출
// 애노테이션 프로퍼티 타입 - 배열 값 지정
package com.eomcs.annotation.ex5;

@MyAnnotation3(
    // 배열 값을 지정할 때 중괄호를 사용한다.
    v1 = {"홍길동", "임꺽정", "유관순"},
    v2 = {1000, 2000, 3000, 4000, 5000},
    v3 = {1.12f, 2.23f, 3, 34f})
public class MyClass4 {
}
package com.eomcs.annotation.ex5;

public class Exam04 {

  public static void main(String[] args) {
    Class<?> clazz = MyClass4.class;
    MyAnnotation3 obj = clazz.getAnnotation(MyAnnotation3.class);

    printValues(obj.v1());
    System.out.println("----------------------");

    printValues(obj.v2());
    System.out.println("----------------------");

    printValues(obj.v3());
  }

  static void printValues(String[] values) {
    for (String value : values) {
      System.out.print(value + ",");
    }
    System.out.println();
  }

  static void printValues(int[] values) {
    for (int value : values) {
      System.out.print(value + ",");
    }
    System.out.println();
  }

  static void printValues(float[] values) {
    for (float value : values) {
      System.out.print(value + ",");
    }
    System.out.println();
  }
}

 

 

애노테이션 프로퍼티 값 추출 - 배열 값 추출
// 애노테이션 프로퍼티 타입 - 배열 값 지정
package com.eomcs.annotation.ex5;

@MyAnnotation3(
    //    v1={"임꺽정"},
    //    v2={1111},
    //    v3={1.11f}

    // 배열 값이 한 개일 경우 중괄호를 생략할 수 있다.
    v1="임꺽정",
    v2=1111,
    v3=1.11f
    )
public class MyClass5 {
}
package com.eomcs.annotation.ex5;

public class Exam05 {

  public static void main(String[] args) {
    Class<?> clazz = MyClass5.class;
    MyAnnotation3 obj = clazz.getAnnotation(MyAnnotation3.class);

    printValues(obj.v1());
    System.out.println("----------------------");

    printValues(obj.v2());
    System.out.println("----------------------");

    printValues(obj.v3());
  }

  static void printValues(String[] values) {
    for (String value : values) {
      System.out.print(value + ",");
    }
    System.out.println();
  }

  static void printValues(int[] values) {
    for (int value : values) {
      System.out.print(value + ",");
    }
    System.out.println();
  }

  static void printValues(float[] values) {
    for (float value : values) {
      System.out.print(value + ",");
    }
    System.out.println();
  }
}

 

 

애노테이션 적용 범위 - 클래스나 인터페이스
package com.eomcs.annotation.ex6;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

// @Target을 사용하여 애노테이션을 붙일 수 있는 범위를 제어할 수 있다.
//@Target(value = {ElementType.TYPE})  // 클래스나 인터페이스 선언에만 붙일 수 있다.
//@Target(value = ElementType.TYPE)  // 한 개의 값만 설정할 경우 중괄호 생략 가능!
@Target(ElementType.TYPE)  // 프로퍼티 이름이 'value'일 경우 이름 생략 가능!
public @interface MyAnnotation {
}
// 애노테이션 프로퍼티 타입
package com.eomcs.annotation.ex6;

// @MyAnnotation은 타입(인터페이스와 클래스)에만 붙일 수 있다.
@MyAnnotation // OK!
public class MyClass {

  //  @MyAnnotation
  int i; // 컴파일 오류!

  //  @MyAnnotation
  public void m(/*@MyAnnotation*/ int p) {
    /*@MyAnnotationn*/ int a;
  }

}

 

 

애노테이션 적용 범위 - 필드
package com.eomcs.annotation.ex6;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
public @interface MyAnnotation2 {
}
// 애노테이션 프로퍼티 타입
package com.eomcs.annotation.ex6;

// @MyAnnotation2은 필드에만 붙일 수 있다.
//@MyAnnotation2
public class MyClass2 {

  @MyAnnotation2 int i;
  @MyAnnotation2 static int i2;


  //  @MyAnnotation2
  public void m(/*@MyAnnotation2*/ int p) {
    /*@MyAnnotation2*/ int a;
  }

}

 

 

애노테이션 적용 범위 - 메서드
package com.eomcs.annotation.ex6;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
public @interface MyAnnotation3 {
}
// 애노테이션 프로퍼티 타입
package com.eomcs.annotation.ex6;

// @MyAnnotation3는 메서드에만 붙일 수 있다.
//@MyAnnotation3
public class MyClass3 {

  /*@MyAnnotation3*/ int i;
  /*@MyAnnotation3*/ static int i2;


  @MyAnnotation3
  public void m(/*@MyAnnotation3*/ int p) {
    /*@MyAnnotation3*/ int a;
  }

}

 

 

애노테이션 적용 범위 - 로컬 변수
package com.eomcs.annotation.ex6;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target(ElementType.LOCAL_VARIABLE)
public @interface MyAnnotation4 {
}
// 애노테이션 프로퍼티 타입
package com.eomcs.annotation.ex6;

// @MyAnnotation4는 로컬 변수에만 붙일 수 있다.
//@MyAnnotation4
public class MyClass4 {

  /*@MyAnnotation4*/ int i;
  /*@MyAnnotation4*/ static int i2;


  //@MyAnnotation4
  public void m(/*@MyAnnotation4*/ int p) {
    @MyAnnotation4 int a;
  }

}

 

 

애노테이션 적용 범위 - 로컬 변수 + 파라미터 + 필드
package com.eomcs.annotation.ex6;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target({ElementType.LOCAL_VARIABLE, ElementType.PARAMETER, ElementType.FIELD})
public @interface MyAnnotation5 {
}
// 애노테이션 프로퍼티 타입
package com.eomcs.annotation.ex6;

// @MyAnnotation5는 로컬 변수, 파라미터, 필드에만 붙일 수 있다.
//@MyAnnotation5
public class MyClass5 {

  @MyAnnotation5 int i;
  @MyAnnotation5 static int i2;


  //@MyAnnotation5
  public void m(@MyAnnotation5 int p) {
    @MyAnnotation5 int a;
  }

}

 

 

com.eomcs.annotation.ex7

 

 

애노테이션 중복 사용 - 기본은 한 번만 가능
package com.eomcs.annotation.ex7;

public @interface Company {
  String value();
}
// 애노테이션 중복 사용
package com.eomcs.annotation.ex7;

@Company("비트캠프")
//@Company("비트캠프") // 중복 사용 불가!
public class MyClass {

  @Company("비트캠프")
  //@Company("비트캠프") // 중복 사용 불가!
  public void m1(int p) {}

  @Company("비트캠프")
  //@Company("비트캠프") // 중복 사용 불가!
  public void m2(int p) {}
}

 

 

애노테이션 중복 사용 - 여러 번 사용 가능
package com.eomcs.annotation.ex7;

import java.lang.annotation.Repeatable;

// 애노테이션을 중복해서 사용할 수 있게 하려면
// - @Repeatable 로 표시해 줘야 한다.
// - 이때 반복에 대한 정보를 따로 정의한 애노테이션을 지정해야 한다.
@Repeatable(value=Employees.class)
public @interface Employee {
  String value();
}
애노테이션 중복 사용 - 중복해서 사용할 애노테이션을 지정한다.
package com.eomcs.annotation.ex7;

public @interface Employees {
  Employee[] value();
}

 

// 애노테이션 중복 사용
package com.eomcs.annotation.ex7;

// @Employees 애노테이션에 @Employee 반복을 정의하였다.
// 그리고 @Employee 애노테이션에 반복할 수 있음을 선언하였다.
// 따라서 다음과 같이 중복 선언할 수 있다.
//
@Employee("홍길동")
@Employee("임꺽정")
public class MyClass2 {

  @Employee("홍길동")
  @Employee("임꺽정")
  public void m1(int p) {}

  @Employee("홍길동")
  @Employee("임꺽정")
  public void m2(int p) {}
}