⦁ 열거형에 멤버 추가하기
- 불연속적인 열거형 상수의 경우, 원하는 값을 괄호()안에 적는다.
enum Direction { EAST(1), SOUTH(5), WEST(-1), NORTH(10) }
괄호()를 사용하려면, 인스턴스 변수와 생성자를 새로 추가해 줘야 한다.
enum Direction {
EAST(1), SOUTH(5), WEST(-1), NORTH(10);
private final int value; // 정수를 저장할 필드(인스턴스 변수)를 추가
Direction (int value) { this.value = value; } // 생성자를 추가
public int getValue() { return value; }
}
- 열거형의 생성자는 묵시적으로 private이므로, 외부에서 객체생성 불가
Direction d = new Direction(1); // 에러, 열거형의 생성자는 외부에서 호출 불가
ex)
enum Direction2 {
EAST(1, ">"), SOUTH(2,"V"), WEST(3, "<"), NORTH(4, "^");
private static final Direction2[] DIR_ARR = Direction2.values();
private final int value;
private final String symbol;
Direction2 (int value, String symbol) { // 접근 제어자 private이 생략됨
this.value = value;
this.symbol = symbol;
}
public int getValue() { return value; }
public String getSymbol() { return symbol; }
public static Direction2 of(int dir) {
if (dir <1 || dir >4)
throw new IllegalArgumentException("Invalid value : " + dir);
return DIR_ARR[dir - 1];
}
// 방향을 회전시키는 메소드. num의 값만큼 90도씩 시계방향으로 회전한다.
public Direction2 rotate(int num) {
num = num%4;
if (num<0) num+=4; // num이 음수일 때는 시계 반대방향으로 회전
return DIR_ARR[(value-1+num) % 4];
}
public String toString() {
return name()+getSymbol();
}
}
public class Ex12_6 {
public static void main(String[] args) {
for(Direction2 d : Direction2.values())
System.out.printf("%s=%d%n", d.name(), d.getValue());
Direction2 d1 = Direction2.EAST;
Direction2 d2 = Direction2.of(1);
System.out.printf("d1=%s, %d%n", d1.name(), d1.getValue());
System.out.printf("d2=%s, %d%n", d2.name(), d2.getValue());
System.out.println(Direction2.EAST.rotate(1));
System.out.println(Direction2.EAST.rotate(2));
System.out.println(Direction2.EAST.rotate(-1));
System.out.println(Direction2.EAST.rotate(-2));
}
}
⦁ 애너테이션이란?
- 주석처럼 프로그래밍 언어에 영향을 미치지 않으며, 유용한 정보를 제공
- 애너테이션의 사용예
@Test // 이 메소드가 테스트 대상임을 테스트 프로그램에게 알린다.
public void method() {
...
}
⦁ 표준 애너테이션
- Java에서 제공하는 애너테이션

(메타 애너테이션은 애너테이션을 만들 때 사용)
⦁ @Override
- 오버라이딩을 올바르게 했는지 컴파일러가 체크하게 한다.
- 오버라이딩할 때 메소드 이름을 잘못적는 실수를 하는 경우가 많다.
- 오버라이딩 할 때는 메소드 선언부 앞에 @Override를 붙이자.
⦁ @Deprecated
- 앞으로 사용하지 않을 것을 권장하는 필드나 메소드에 붙인다.
- @Deprecated의 사용 예, Date클래스의 getDate()
- @Deprecated가 붙은 대상이 사용된 코드를 컴파일하면 나타나는 메시지
Note : AnnotationEx2.java uses or overrides a deprecated API.
Note : Recompile with –Xlint:deprecation for details.
Xlint:deprecation을 추가해서 다시 컴파일하면 다음과 같이 원인이 나온다.

⦁ @FunctionalInterface
- 함수형 인터페이스에 붙이면, 컴파일러가 올바르게 작성했는지 체크
- 함수형 인터페이스에는 단 하나의 추상메소드만 가져야 한다는 제약이 있음
@FunctionalInterface
public interface Runnable {
public abstract void run(); // 추상 메소드
}
⦁ @SuppressWarnings
- 컴파일러의 경고메세지가 나타나지 않게 억제한다.
- 괄호()안에 억제하고자하는 경고의 종류를 문자열로 지정
@SuppressWarnings(“unchecked”) // 지네릭스와 관련된 경고를 억제
ArrayList list = new ArrayList(); // 지네릭 타입을 지정하지 않았음.
list.add(obj); // 여기서 경고가 발생
- 둘 이상의 경고를 동시에 억제하려면 다음과 같이 한다.
@SuppressWarnings({"deprecation", "unchecked", "varargs"})
- ‘-Xlint’ 옵션으로 컴파일하면, 경고메세지를 확인할 수 있다.
괄호[]안이 경고의 종류. 아래의 경우 rawtypes

ex)
import java.util.ArrayList;
class Parent {
void parentMethod() {}
}
class Child extends Parent {
@Override
@Deprecated
void parentMethod() {} // 조상 메소드의 이름을 잘못 적으면 오류가 발생함.
}
@FunctionalInterface // 함수형 인터페이스는 하나의 추상메소드만 가능
interface Testable {
void test(); // 추상메소드
// void check(); // 추상메소드
}
class Ex12_7 {
@SuppressWarnings("deprecation")
public static void main(String[] args) {
Child c = new Child();
c.parentMethod(); // deprecated된 메소드 사용
}
}
⦁ 메타 애너테이션
- 메타 애너테이션은 ‘애너테이션을 위한 애너테이션’

⦁ @Target
- 애너테이션을 정의할 때, 적용대상 지정에 사용

@Target({FIELD, TYPE, TYPE_USE}) // 적용대상이 FIELD, TYPE, TYPE_USE
public @interface MyAnnotation { } // MyAnnotation을 정의
@MyAnnotation // 적용대상이 TYPE인 경우
class MyClass {
@MyAnnotation // 적용대상이 FIELD인 경우
int i;
@MyAnnotation // 적용대상이 TYPE_USE인 경우
MyClass mc;
}
⦁ @Retention
- 애너테이션이 유지(retention)되는 기간을 지정하는데 사용

- 컴파일러에 의해 사용되는 애너테이션의 유지 정책은 SOURCE이다.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {}
(오버라이딩 체크는 컴파일러가 컴파일 할 때 함. 그러므로 유지정책이 SOURCE)
- 실행시에 사용 가능한 애너테이션의 정책은 RUNTIME이다.
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}
⦁ @Documented, @Inherited
- javadoc으로 작성한 문서에 포함시키려면 @Documented를 붙인다.
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}
- 애너테이션을 자손 클래스에 상속하고자 할 때, @Inherited를 붙인다.
@Inherited // @SuperAnno가 자손까지 영향 미치게
@interface SuperAnno {}
@SuperAnno
class Parent {}
class Child extends Parent {} // Child에 애너테이션이 붙은 것으로 인식
⦁ @Repeatable
- 반복해서 붙일 수 있는 애너테이션을 정의할 때 사용
@Repeatable(ToDos.class) // ToDo애너테이션을 여러 번 반복해서 쓸 수 있게 한다.
@interface ToDo {
String value();
}
- @Repeatable이 붙은 애너테이션은 반복해서 붙일 수 있다.
@ToDo("delete test codes.")
@ToDo("override inherited methods")
class MyClass {
...
}
- @Repeatable인 @ToDo를 하나로 묶을 컨테이너 애너테이션도 정의해야 함
@interface ToDos { // 여러개의 ToDo애너테이션을 담을 컨테이너 애너테이션 ToDos
ToDo[] value(); // ToDo애너테이션 배열타입의 요소를 선언. 이름이 반드시 value이어야 함
}
⦁ 애너테이션 타입 정의하기
- 애너테이션을 직접 만들어 쓸 수 있다.
@interface 애너테이션이름 {
타입 요소이름(); // 애너테이션의 요소를 선언한다.
...
}
@interface DateTime {
String yymmdd();
String hhmmss();
}
- 애너테이션의 메소드는 추상 메소드이며, 애너테이션을 적용할 때 지정(순서X)
@interface TestInfo {
int count();
String testedBy();
String[] testTools();
TestType testType(); // enum TestType { FIRST, FINAL }
DateTime testDate(); // 자신이 아닌 다른 애너테이션(@DateTime)을 포함할 수 있다.
실제 사용할 때)
@TestInfo (
Count=3, testedBy="Kim",
testTools={"JUnit", "AutoTester"},
testType=TestType.FIRST,
testDate=@DateTime(yymmdd="160101", hhmmss="235959")
)
public class NewClass { ... }
⦁ 애너테이션의 요소
- 적용시 값을 지정하지 않으면, 사용될 수 있는 기본값 지정 가능(null제외)
@interface TestInfo {
int count() default 1; // 기본값을 1로 지정
}
@TestInfo // @TestInfo(count=1)과 동일
public class NewClass { ... }
- 요소가 하나이고 이름이 value일 때는 요소의 이름 생략가능
@interface TestInfo {
String value();
}
@TestInfo("passed") // @TestInfo(value="passed")와 동일
class NewClass { ... }
- 요소의 타입이 배열인 경우, 괄호{}를 사용해야 한다.
@interface TestInfo {
String[] testTools();
}
@TestInfo(testTools={"JUnit", "AutoTester"}) // 값이 2개 이상일 때
@TestInfo(testTools="JUnit") // 값이 1개일 때
@TestInfo(testTools={}) // 값이 없을 때는 괄호{}가 반드시 필요
⦁ 모든 애너테이션의 조상
- Annotation은 모든 애너테이션의 조상이지만 상속은 불가
- Annotation은 인터페이스이다.
package java.lang.annotation;
public interface Annotation {
boolean equals(Object obj);
int hashCode();
String toString();
Class<? extends Annotation> annotationType(); // 애너테이션의 타입을 반환
}
(모든 에너테이션은 추상메소드를 구현하지 않아도 사용가능)
⦁ 마커 애너테이션 – Marker Annotation
- 요소가 하나도 정의되지 않은 애너테이션
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {} // 마커 애너테이션, 정의된 요소가 하나도 없다.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Test {} // 마커 애너테이션, 정의된 요소가 하나도 없다.
@Test // 이 메소드가 테스트 대상임을 테스트 프로그램에게 알린다.
public void method() {
...
}
@Deprecated
public int getDate() {
return normalize().getDayOfMonth();
}
⦁ 애너테이션 요소의 규칙
- 애너테이션의 요소를 선언할 때 아래의 규칙을 반드시 지켜야 한다.
요소의 타입은 기본형, String, enum, 애너테이션, Class만 허용됨
괄호()안에 매개변수를 선언할 수 없다.
예외를 선언할 수 없다.
요소를 타입매개변수(<T>)로 정의할 수 없다.
- 아래의 코드에서 잘못된 부분은 무엇인지 생각해보자.
@interface AnnoTest {
int id = 100; // 상수 OK
String major(int a, int b); // 에러, 매개변수X
String minor() throws Exception; // 에러, 예외선언X
ArrayList<T> list(); // 에러, 타입매개변수 정의X
}
ex)
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Deprecated
@SuppressWarnings("1111") // 유효하지 않은 애너테이션은 무시된다.
@TestInfo(testedBy="aaa", testDate=@DateTime(yymmdd="160101",hhmmss="235959"))
public class Ex12_8 {
public static void main(String[] args) {
// Ex12_8의 Class객체를 얻는다.
Class<Ex12_8> cls = Ex12_8.class;
TestInfo anno = cls.getAnnotation(TestInfo.class);
System.out.println("anno.testedBy()="+anno.testedBy());
System.out.println("anno.testDate().yymmdd()="+anno.testDate().yymmdd());
System.out.println("anno.testDate().hhmmss()="+anno.testDate().hhmmss());
for(String str : anno.testTools())
System.out.println("testTools="+str);
System.out.println();
// Ex12_8에 적용된 모든 애너테이션을 가져온다.
Annotation[] annoArr = cls.getAnnotations();
for(Annotation a : annoArr)
System.out.println(a);
}
}
@Retention(RetentionPolicy.RUNTIME) // 실행 시에 사용가능하도록 지정
@interface TestInfo {
int count() default 1;
String testedBy();
String[] testTools() default "JUnit";
TestType testType() default TestType.FIRST;
DateTime testDate();
}
@Retention(RetentionPolicy.RUNTIME) // 실행 시에 사용가능하도록 지정
@interface DateTime {
String yymmdd();
String hhmmss();
}
enum TestType { FIRST, FINAL }
출처 - 유튜브 남궁성의 정석코딩 [자바의 정석 - 기초편]
'Java > Java의 정석' 카테고리의 다른 글
220329 Java - Chapter 13. 쓰레드(Thread) 남은 부분 (0) | 2022.03.29 |
---|---|
220328 Java - Chapter 13. 쓰레드(Thread) (0) | 2022.03.29 |
220324 Java - Chapter 12. 지네릭스, 열거형, 애너테이션 Part.2 (0) | 2022.03.24 |
220323 Java - Chapter.12 지네릭스, 열거형, 애너테이션 Part.1 (0) | 2022.03.23 |
220322 Java - Chapter.11 Collections Framework Part.3 (0) | 2022.03.23 |