[자바의 정석] 지네릭스, 열거형, 애너테이션

27070 단어 JavaJava

열거형 enums

서로 관련된 상수를 편리하게 선언하기 위한 것으로 C언어의 열거형보다 더 향상되어 열거형이 갖는 값뿐만 아니라 타입도 관리

class Card{
	enum Kind {Clover, Heart, Diamond, Spade} //0,1,2,3
    enum Value {Two, Three, Four} //0,1,2
    
    final Kind kind; //상수이기 때문에 final
    final Value value;
}

자바의 열거형은 타입이 다르더라도 값이 값이 같으면 True를 리턴하는 것과 달리 값과 타입이 모두 같아야 에러가 발생하지 않는다.

if(Card.Clover == Card.Two) // Card 타입(타입 갖고), Clover(0) == Two(0) → True
if (Card.Kind.Clover == Card.Value.Two) // Kind와 Value타입이 다르므로 → False

열거형의 중요한 점 하나로 원래는 상수의 값이 바뀌면, 해당 상수를 참조하는 모든 소스를 다시 컴파일 해야하지만 열거형 상수를 사용하면 기존의 소스를 다시 컴파일 할 필요가 없다.

열거형 정의

// 열거형 정의
enum Direction {EAST, WEST, SOUTH, NORTH}

class Unit{
	int x, y;
    Direction dir; // 열거형을 인스턴스로 생성
    
    void init(){
    	dir = Direction.EAST;
        
        if (dir == Direction.EAST){ //열거형은 ==으로 비교가능
        	x++;
        }
        else if (dir > Direction.WEST){ //열거형은 비교연산 사용 불가
        }
        else if (dir.compareTo(Direction.WEST) > 0){ // compateTo로 비교
        }
    }
}

열거형 멤버 추가

Enum Class에 정의된 ordinal()이 열거형 상수가 정의된 순서를 반환(0부터 1,2,3)하지만, 이 값을 열거형 상수의 값으로 사용하지 않는 것이 좋다.
불연손적일 경우에는 다음과 같이 사용한다.

enum Direction {EAST(1), SOUTH(5), WEST(-1), NORTH(10)}
enum Direction { 
	//반드시 ';' 붙여야 한다.
	EAST(1, ">"), SOUTH(2,"V"), WEST(3, "<"), NORTH(4,"^");
	// 먼저 열거형 상수를 정의를 한 뒤에 멤버를 추가해야 한다.
	private static final Direction[] DIR_ARR = Direction.values();
	private final int value;
	private final String symbol;

	Direction(int value, String symbol) { // private Direction(int value)
		this.value  = value;
		this.symbol = symbol;
	}

	public int getValue()      { return value;  }
	public String getSymbol()  { return symbol; }

	public static Direction of(int dir) {
        if (dir < 1 || dir > 4) {
            throw new IllegalArgumentException("Invalid value :" + dir);
        }
        return DIR_ARR[dir - 1];		
	}	

	// 방향을 회전시키는 메서드. num의 값만큼 90도씩 시계방향으로 회전한다.
	public Direction 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();
	}
} // enum Direction

위에 코드는 단순히 enum에 멤버 변수를 어떻게 추가하고, 추가하기 위한 제약사항을 설명하기 위한 코드이다.

열거형에 추상메서드

// 열거형으로 운송수단을 선언했다.
// 각 운송수단마다 요금이 다르기 때문에 요금을 계산하는 추상메서드를 선언해야한다.
enum Transportation { 
	BUS(100)      { int fare(int distance) { return distance*BASIC_FARE;}},
	TRAIN(150)    { int fare(int distance) { return distance*BASIC_FARE;}},
	SHIP(100)     { int fare(int distance) { return distance*BASIC_FARE;}},
	AIRPLANE(300) { int fare(int distance) { return distance*BASIC_FARE;}};

	protected final int BASIC_FARE; // protected로 해야 각 상수에서 접근가능
	//생성자	
	Transportation(int basicFare) { // private Transportation(int basicFare) {
		BASIC_FARE = basicFare;
	}

	public int getBasicFare() { return BASIC_FARE; }
	// 추상메서드 선언
	abstract int fare(int distance); // 거리에 따른 요금 계산
}

열거형에 추상메서드를 선언하는 일은 많지 않으므로 참고만!

애너테이션 (annotation)

소스코드 안에 다른 프로그램을 위한 정보를 미리 약속된 형식으로 포함시킨 것
주석처럼 프로그래밍 언어에 영향을 미치지 않으면 유용한 정보를 제공

표준 애너테이션

  • @Override
    : 메서드 앞에만 붙을 수 있는 애너테이션으로, 조상의 메서드를 오버라이딩 하는 것

    오버라이팅 할 때 메서드의 이름을 잘못 작성할 경우가 있는데, 컴파일러는 오타로 인식하는 것이 아닌 새로운 매서드가 추가된 것으로 인식한다. 하지만 @Override를 사용하면, 컴파일러가 같은 이름의 메서드가 있는지 확인하고 없으면 에러 발생

  • @Deprecated
    : 다른 것으로 대체된 필드나 매서드에 붙여 이제 사용되지 않는 다는 것을 알린다.

  • @FunctionalInterface
    : 함수형 인터페이스를 선언할 때, 컴파일러가 인터페이스를 올바르게 선언했는지 확인할 때 사용

@FunctionalInterface
public interface Runnable {
	public abstract void run();
}
//FunctionalInterface 애너테이션이 있으므로 인터페이스의 추상메서드가 1개보다 많을 경우 애러 발생
  • @SuppressWarnings
    : 컴파일러가 보여주는 경고메시지가 나타나지 않게 억제. 경고가 발생한 것을 알면서도 묵인할 때 사용
    억제 범위는 최소한으로 해야 추가된 코드에서 발생한 경고를 확인할 수 있다.
@SuppressWarnings({"deprecation", "unchecked"})
ArrayList list = new ArrayList(); //지네릭 타입을 지정하지 않아 경고가 발생하나 묵인
list.add(obj);
  • @SafeVarargs
    : 메서드에 선언된 가변인자의 타입이 non-reifiable타입일 경우, 해당 메서드를 선언하는 부분과 호출하는 부분에서 unchecked 경고가 발생하는데, 해당 코드에 문제가 없다면 경고를 출력하지 않기 위해 사용

메타 애너테이션

애너테이션에 붙이는 애너테이션으로 애너테이션을 정의할 때 애너테이션의 적용대상이나 유지기간을 지정

@Target

애너테이션이 적용가능한 대상을 지정하는데 사용

@Target({TYPE, FILED, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})

TYPE: 타입을 선언할 때 / TYPE_USE: 타입의 변수를 선언할 때

@Target ({FIELD, TYPE, TYPE_USE})
public @interface MyAnnotation(){}
@MyAnnotation()
class MyClass{ //Type
	@MyAnnotation()
    	int i; // FIELD : 필드는 기본형
    	@MyAnnotation()
        MyClass mc; //TYPE_USE : 타입 변수 선언
}

@Retention

애너테이션이 유지되는 기간을 지정

  1. SOURCE
    : @Override 또는 @SuppressWarnings 같이 컴파일러가 사용하는 애너테이션에는 Source

  2. RUNTIME
    : 실행시에 리플렉션을 통해 클래스 파일에 저장된 애니터이션의 정보를 읽어서 처리

  3. CLASS
    : 컴파일러가 애너테이션의 정보를 클래스 파일에 저장할 수 있게는 하지만, JVM이 로딩될 때는 애너테이션의 정보가 무시되어 실행시에 애너테이션 정보를 얻을 수 없다.
    → 그래서 잘 안씀

@Inherited

애너테이션이 자손 클래스에 상속되도록 하며, 만약 조상클래스에 Inherited를 붙이면 자손클래스에도 애너테이션이 적용된 것과 같다.

@Repeatable

하나의 대상에 여러 애너테이션을 적용할 때 사용

@Repeatable(ToDos.class)
@interface ToDo{
	 String value();
}

@ToDo("Delete")
@ToDo("Override")
class MyClass{}

애너테이션 타입 정의

@interface 애너테이션이름{
	타입요소이름(); //애너테이션 요소
}

애너테이션을 정의하는 방법은 인터페이스를 정의하는 것과 같고 앞에 @만 붙이면 된다.

@interface DateTime{
	String yymmdd();
    	String hhmmss();
}

모든 애너테이션의 조상은 Annotation인데 이는 상속을 허용하지 않으므로 extends Annotation으로 명시할 수 없다.

애너테이션 요소 규칙

요소의 타입은 기본형, String, enum, 애너테이션, class만 가능
()안에 매개변수를 선언 불가
예외 선언 불가
요소를 타입 매개변수로 정의 불가

@interface AnnoTest{
	int id = 100; //상수 선언 가능 ⭕️
    String major(int i, int j); // 매개변수 선언 불가 ❌
    String minor() throws Excpetion; //예외불가 ❌
    ArrayList<T> list(); //요소 타입에 타입 매개변수 불가 ❌
}

좋은 웹페이지 즐겨찾기