: 서로 관련된 상수들을 같이 묶어 놓은 것.
class Card {
static final int CLOVER = 0;
static final int HEART = 1;
static final int DIAMOND = 2;
static final int SPADE = 3;
static final int TWO = 0;
static final int THREE = 1;
static final int FOUR = 2;
}
열거형 사용🔽
class Card {
enum Kind {CLOVER, HEART, DIAMOND, SPADE}
enum Value {TWO, THREE, FOUR}
final Kind kind;
final Value value;
}
if(Card.CLOVER == Card.TWO) // true
if(Card.Kind.CLOVER == Card.Value.TWO) // 컴파일 에러
정의
enum 열거형 이름 {상수명1, 상수명2 ...}
예시) enum Direction {EAST, SOUTH, WEST, NORTH}
열거형 타입의 변수를 선언하고 사용
class Unit {
Direction dir = Direction.EAST;
...
}
열거형 상수의 비교는 ==
와 compareTo()
사용 가능
if (dir == Direction.EAST) { // OK
...
} else if (dir < Direction.WEST) { // 에러
...
} else if (dir.compareTo(Direction.SOUTH) > 0 ) { // OK
...
}
else if (dir < Direction.WEST)
: 열거형 상수에는 비교연산자 사용 불가모든 열거형이 상속 받는 메서드들
메서드 | 설명 |
---|---|
Class<E> getDeclaringClass() | 열거형의 Class 객체를 반환 |
String name() | 열거형 상수의 이름을 문자열로 반환 |
int ordinal() | 열거형 상수가 정의된 순서를 반환(0부터 시작) |
T valueOf(Class<T> enumType, String name) | 지정된 열거형에서 name과 일치하는 열거형 상수를 반환 |
컴파일러가 자동으로 추가해주는 메서드
메서드 | 설명 |
---|---|
static E[] values() | 열거형 상수가 가지고 있는 모든 값들을 배열로 반환 |
static E valueOf(String name) | 열거형 상수 이름(name)을 반환 |
열거형 상수 정의 및 변수 선언
enum Direction {EAST, SOUTH, WEST, NORTH} // values = 0, 1, 2, 3
class Ex12_5 {
public static void main(String[] args) {
Direction d1 = Direction.EAST;
Direction d2 = Direction.valueOf("WEST");
Direction d3 = Enum.valueOf(Direction.class, "EAST");
System.out.println("d1 = " + d1);
System.out.println("d2 = " + d2);
System.out.println("d3 = " + d3);
...
d1 = EAST
d2 = WEST
d3 = EAST
값을 따로 지정해주지 않으면 자동으로 0에서부터 값이 할당된다.
Direction d1 = Direction.EAST;
: 보통 이 방법을 제일 많이 쓴다. 나머지 2가지 방법도 기능은 똑같다.
열거형 상수 비교
...
System.out.println("d1 == d2 ? " + (d1 == d2));
System.out.println("d1 == d3 ? " + (d1 == d3));
System.out.println("d1.equals(d3) ? " + d1.equals(d3));
// System.out.println("d2 > d3 ? "+ (d1 > d3)); // 컴파일 에러
System.out.println("d1.compareTo(d3) ? " + (d1.compareTo(d3)));
System.out.println("d1.compareTo(d2) ? " + (d1.compareTo(d2)));
...
d1 == d2 ? false
d1 == d3 ? true
d1.equals(d3) ? true
d1.compareTo(d3) ? 0
d1.compareTo(d2) ? -2
values() 메서드
...
Direction[] dArr = Direction.values();
for (Direction d : dArr)
System.out.printf("%s = %d%n", d.name(), d.ordinal());
...
EAST = 0
SOUTH = 1
WEST = 2
NORTH = 3
Direction.values()
: 열거형의 모든 상수를 배열로 반환
for (Direction d : dArr)
: for (Direction d : Direction.values()) 와 동일
d.name()
: 열거형 상수의 이름 반환
d.ordinal()
: 열거형 상수의 순서 반환(주의 : 값 아님)
enum Direction {
EAST(1, "▶"), SOUTH(2, "▼"), WEST(3, "◀"), NORTH(4, "▲");
private final int value;
private final String symbol;
Direction(int value, String symbol) {
this.value = value;
this.symbol = symbol;
}
public int getValue() {
return value;
}
public String getSymbol() {
return symbol;
}
}
EAST(1, "▶"), SOUTH(2, "▼"), WEST(3, "◀"), NORTH(4, "▲");
: 원하는 값을 괄호안에 적는다.
private final int value;
, private final String symbol;
: 괄호안에 들어갈 값을 저장할 인스턴스 변수를 추가해준다.
Direction(int value, String symbol) {
this.value = value;
this.symbol = symbol;
}
Direction d = new Direction(1, "▶");
: 에러. 외부에서 호출 불가public int getValue() {
return value;
}
public String getSymbol() {
return symbol;
}
public class EnumExample {
public static void main(String[] args) {
for (Direction dir : Direction.values()) {
System.out.printf("%-5s : value : %d, symbol : %s, ordinal: %d%n",
dir.name(), dir.getValue(), dir.getSymbol(), dir.ordinal());
}
}
}
dir.getValue()
와 dir.ordinal()
차이dir.getValue()
는 열거형 상수 괄호안에 들어간 값dir.ordinal()
는 열거형 상수들의 순서(0부터 시작): 주석처럼 프로그래밍 언어에 영향을 미치지 않으며, 유요한 정보를 제공
: Java에서 제공하는 애너테이션
애너테이션 | 설명 |
---|---|
@Override | 해당 메서드를 오버라이딩 하는 것이다. |
@Deprecated | 앞으로 사용하지 않을 것을 권장한다. |
@SuppressWarnings | 컴파일러 특정 경고메세지 차단 |
@SafeVarargs | 지네릭스 타입의 가변인자 사용 |
@FunctionalInterface | 함수형 인터페이스이다. |
@Native | native메서드에서 참조되는 상수 앞에 붙인다. |
애너테이션 만들 때 사용하는 메타 애너테이션
애너테이션 | 설명 |
---|---|
@Taget* | 애너테이션 적용 가능 대상 지정 |
@Documented* | 해당 애너테이션 정보가 javadoc 작성 문자에 포함되게끔 |
@Inherited* | " 자손 클래스 "에상속되도록 |
@Retention* | 애너테이션 유지 범위 지정 |
@Repeatable* | 애너테이션 반복 적용 허용 |
오버라이딩을 제대로 했는지 컴파일러가 체크하게 한다.
오버라이딩할 때 메서드 이름을 잘못 적는 실수를 예방
class Product {
void operate() {}
}
class Tv extends Product {
@Override
void opearte() {} // 컴파일 에러
}
@Override
애너테이션을 안붙이면 철자 실수로 인해 내 작성 의도와 다르게 새로운 메서드를 작성한 것처럼 되서 컴파일은 정상적으로 작동한다.@Override
를 붙임으로써 작성한 메서드가 조상 클래스에 없는 클래스라고 컴파일러가 알려준다.앞으로 사용하지 않을 것을 권장하는 필드나 메서드에 붙인다.
class Parent {
void parentMethod() { }
}
class Child extends Parent {
@Override
@Deprecated
void parentMethod() { }
}
public class Ex12_7 {
public static void main(String[] args) {
Child child = new Child();
child.parentMethod();
}
}
deprecated API
를 썼다는 경고문구가 뜬다. javac -Xlint:deprecation Ex12_7.java
로 더 자세한 경고 내용을 볼 수 있다.함수형 인터페이스는 단 하나의 추상 메서드만 가질 수 있다. 이 애너테이션을 붙이면 컴파일러가 제대로 작성했는지 체크해준다.
@FunctionalInterface
interface Testable {
void test();
void test2(); // 컴파일 에러
}
@FunctionalInterface
이 애너테이션을 붙인 인터페이스가 2개 이상의 추상 메서드를 작성하면 에러가 난다.컴파일러의 경고 메시지가 나타나지 않게 억제
@SuppressWarnings("unchecked")
ArrayList list = new ArrayList();
list.add(1);
괄호 안에 억제하고자 하는 경고의 종류를 문자열로 지정한다.
@SuppressWarnings("unchecked")
: 제네릭스와 관련된 경고를 억제
경고가 실행에 영향을 주진 않지만 내가 이 경고를 보고 확인했다는 의미에서 이 애너테이션을 붙인다.
둘 이상의 경고를 동시에 억제하는 법
@SuppressWarnings({"deprecation", "unchecked", "varargs"})
애너테이션을 정의할 때 사용하는 애터네이션. java.lang.annotation 패키지에 포함
애너테이션을 정의할 때, 적용 대상 지정에 사용된다.
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
@SuppressWarnings
애너테이션의 적용 대상 타입 설정해준다.대상 타입 | 설명 |
---|---|
ANNOTAION_TYPE | 애너테이션 |
CONSTRUCTOR | 생성자 |
FIELD | 필드 (iv, cv) |
LOCAL_VARIABLE | 지역변수 |
METHOD | 메서드 |
PACKAGE | 패키지 |
PARAMETER | 매개변수 |
TYPE | 타입(class, interface) |
🔽 JDK1.8 이상부터 가능 🔽 | ㅡㅡㅡㅡㅡㅡㅡㅡㅡ |
TYPE_PARAMETER | 타입 매개변수 |
TYPE_USE | 타입이 사용되는 모든 곳 |
애너테이션이 유지(retention)되는 기간을 정하는데 사용
유지 기준 | 의미 |
---|---|
SOURCE | 소스파일에만 존재. 클래스 파일에 X(애너테이션 정보가 클래스 파일에 남아있지 않음.) |
CLASS(기본값) | 클래스 파일에 존재 -> 실행시 사용 불가 |
RUNTIME | 클래스 파일에 존재 -> 실행시 사용 가능 |
CLASS
는 잘 쓰이지 않고 SOURCE
와 RUNTIME
이 자주 사용된다.컴파일러에 의해 사용되는 애너테이션의 유지 정책은 SOURCE
이다.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {}
@Override
는 컴파일러가 오버라이딩 체크하고 끝이기 때문에 실행 시에 필요가 없다. 따라서 유지 정책이 클래스 파일에 존재하지 않는 SOURCE
이다.실행 시 사용 가능한 애너테이션의 유지 정책은 RUNTIME
이다.
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}
RUNTIME
유지 정책은 실행 시에만 존재하는게 아니라 컴파일 타임부터 실행 시까지 유지되는 것이다.@Documented
: javadoc으로 작성한 문서에 포함시킬 때 붙인다.
@Inherited
: 애너테이션을 자손 클래스에 상속하고자할 때 붙인다.
@Inherited
@interface MyAnno{}
@MyAnno
class Parent{}
class Child extends Parent{} // Parent클래스에 붙어있던 MyAnno가 동일하게 붙은 것으로 인식
@MyAnno
이 붙어있지 않지만 @Inherited
으로 인해 조상의 애너테이션을 상속받아 @MyAnno
이 붙어있는 것으로 인식된다.반복해서 붙일 수 있는 애너테이션을 정의할 때 사용
// @ToDo를 @Repeatable으로 지정
@Repeatable(ToDos.class)
@interface ToDo {
String value();
}
// 여러번 반복해서 붙일 수 있다.
@ToDo("update codes")
@ToDo("override inherited methods")
class MyClass {
...
}
@Repeatable
이 붙은 애너테이션으 반복해서 붙일 수 있다.// @ToDo를 하나로 묶을 컨테이너 애너테이션
@interface ToDos {
ToDo[] value(); // 반드시 value
}
@Repeatable
로 반복해서 붙일 수 있도록 정의한 애너테이션을 하나로 묶을 컨테이너 애너테이션도 정의해줘야 한다.@interface 애너테이션 이름 {
타입 요소이름();
...
}
애너테이션의 메서드는 추상 메서드이며, 애너테이션을 적용할 때 지정(순서X)
enum TestType { FIRST, FINAL }
@interface DateTime {
String yymmdd() ;
String hhmmss() ;
}
@interface Test {
int count(); // 정수
String testedBy() ; // 문자열
String[] testTools() ; // 문자열 배열
TestType testType(); // Enum
DateTime testDate(); // 다른 애너테이션
}
@Test(
count = 3,
testedBy = "Jung",
testTools = {"JUnit", "AutoTester"},
testType = TestType.FIRST
testDate = @DateTime(yymmdd="", hhmmss="")
)
public class NewTest {
... // Test 애너테이션 사용가능
}
적용 시 값을 지정하지 않으면, 사용될 수 있는 기본값 지정 가능(null 제외)
@interface Test {
int count() default 1;
}
@Test
public class NewTest {
... // Test 애너테이션 사용가능
}
@Test(count=1)
이런 식으로 애너테이션의 정의된 요소의 값을 전부 정의해줘야 한다. 하지만 애너테이션을 생성할 때 default 값을 줬다면 @Test
이런 식으로만 써줘도 된다.요소가 하나이고 이름이 value일 때는 요소의 이름을 생략할 수 있다.
@interface Test {
String value();
}
@Test("passed")
public class NewTest {
... // Test 애너테이션 사용가능
}
@Test(value="passed")
라고 적어줘야 하지만 요소가 하나이고 그 요소의 이름이 value
일 때는 요소의 이름을 생략하고 값만 적어줘도 된다.요소의 타입이 배열인 경우, 중괄호{}를 사용해야 한다.
@interface Test {
String[] testTools();
}
@Test(testTools={"JUnit", "Auto"})
public class NewTest {
... // Test 애너테이션 사용가능
}
@Test(testTools={"JUnit", "Auto"})
: 배열의 값을 적어줄 때 중괄호를 적어줘야 한다.
@Test(testTools="JUnit")
: 배열의 값이 1개일 때는 중괄호 생략 가능
@Test(testTools={})
: 배열에 값이 없을 때는 반드시 빈 중괄호{}
를 적어줘야 한다.
Annotation 인터페이스는 모든 애너테이션의 조상이지만 상속은 불가하다.
package java.lang.annotation;
public interface Annotation {
boolean equals(Object obj);
int hashCode();
String toString();
Class<? extends Annotation> annotationType();
}
Class<? extends Annotation> annotationType();
: 애너테이션의 타입을 반환요소가 하나도 정의되지 않은 애너테이션
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {} // 정의된 요소가 하나도 없다.
@Override
: 대표적인 마커 애너테이션애너테이션의 요소를 선언할 때 지켜야할 규칙들
요소의 타입은 기본형, String, enum, 애너테이션, Class(설계도 객체)만 허용된다.
괄호()안에 매개변수를 선언할 수 없다.
예외를 선언할 수 없다.
요소를 타입 매개변수로 정의할 수 없다.