[윤성우의 열혈 Java 프로그래밍] Chapter 25 - 열거형, 가변 인자 그리고 어노테이션

Update:     Updated:

카테고리:

태그:

25-1. 열거형

열거형: ‘의미가 부여된 이름’을 갖는 ‘상수’의 선언

- 인터페이스 기반의 상수 정의

열거형 등장 이전의 모습을 보자.

interface Animal {
   int DOG = 1;  
   int CAT = 2; 
}

interface Person {
   int MAN = 1;
   int WOMAN = 2;
}

class NonSafeConst {
    public static void main(String[] args) {
        who(Person.MAN);    // 정상적인 메소드 호출
	who(Animal.DOG);    // 비정상적 메소드 호출
    }
	
    public static void who(int man) {
        switch(man) {
        case Person.MAN:
            System.out.println("남성 손님입니다.");
            break;
        case Person.WOMAN:
            System.out.println("여성 손님입니다.");
            break;
        }
    }
}

열거형 등장 이전에는 보다싶이 인터페이스를 이용하여 의미를 가진 상수를 표현했다.
다만, 이런 방식의 경우 위의 코드와 같이 개발자의 의도와는 다르게
Person.MAN에 Animal.DOG이 전달되더라도 컴파일 단계에서 오류가 발견되지 않는다.

- 자료형의 부여를 돕는 열거형

enum Scale {
    DO, RE, MI, FA, SO, RA, TI
}

열거형은 다음과 같이 정의한다.
그 안에 위치한 이름들을 가리켜 ‘열거형 값’, Enumerated Values라고 한다.

다음과 같이 Scale형 변수를 선언하고, switch문을 구성할 수도 있다.

enum Scale {
   DO, RE, MI, FA, SO, RA, TI
}

class SimpleEnum {
    public static void main(String[] args) {
        Scale sc = Scale.DO;    // Scale형 참조변수 선언
        System.out.println(sc);

        switch(sc) {
        case DO:
            System.out.println("도~ ");
            break;
        case RE:
            System.out.println("레~ ");
            break;
        case MI:
            System.out.println("미~ ");
            break;
        case FA:
            System.out.println("파~ ");
            break;
        default:
            System.out.println("솔~ 라~ 시~ ");
        }
    }
}

- 클래스 내에 정의가 가능한 열거형의 정의

특정 클래스 내에서만 사용하고 싶은 열거형 값이 있다면, 해당 클래스 내에 열거형을 정의하는 방법도 있다.

class Customer {
    enum Gender { MALE, FEMALE }    // 클래스 내부에서 열거형 선언

    private String name;
    private Gender gen;
    
    Customer(String n, String g) {
        name = n;
        
        if(g.equals("man"))
            gen = Gender.MALE;
        else
            gen = Gender.FEMALE;
    }

    @Override
    public String toString() {
        if(gen == Gender.MALE)
            return "Thank you, Mr " + name;
        else
            return "Thank you, Mrs " + name; 
    }    
}

class InnerEnum {
    public static void main(String[] args) {
        Customer cus1 = new Customer("Brown", "man");
        Customer cus2 = new Customer("Susan Hill", "woman");
  
        System.out.println(cus1); 
        System.out.println(cus2); 
    }
}

- 열거형 값의 정체

enum Human {
    MAN(29), WOMAN(25);
    
    int age;

    private Human(int age) {
        this.age = age;
        System.out.println("Human Constructor called");
    }

    @Override
    public String toString() {
        return "I am " + age + " years old!";
    }
}

public class EnumExample {
    public static void main(String[] args) {
        System.out.println(Human.MAN);
        System.out.println(Human.WOMAN);
    }
}

image

위의 실행 결과를 통해 다음을 알 수 있다.

  1. 생성자가 호출되었다.
  2. toString 메소드가 호출되었다.

모든 열거형은 java.lang.Enum 클래스를 상속하고, Enum는 Object 클래스를 상속한다. 이런 측면에서 볼 때 열거형은 넓게 보면 클래스라고도 할 수 있다. 또한, toString이 호출된 것으로 볼 때 Human.Man, Human.WOMAN 두 열거형 값은 Human 인스턴스를 참조하는 참조변수임을 알 수 있다.

열거형의 정의에도 생성자가 없으면 디폴트 생성자가 삽입되지만, 이 생성자는 private으로 선언되어 직접 인스턴스를 생성하는 것이 불가능할 뿐이다.

정리하자면,

  • 열거형도 Object 클래스를 상속하는 일종의 클래스이다.
  • 따라서 생성자는 물론, 인스턴스 변수와 메소드 둘 다 가질 수 있다.
  • 다만 모든 생성자를 private로 선언해야 하기 때문에 ‘열거형 값’이 유일한 인스턴스 생성 방법이라는 차이가 있을 뿐이다.

25-2. 매개변수의 가변 인자 선언

…이 삽입된 이 메소드의 매개변수 선언을 가리켜 ‘가변 인자 선언’이라 한다.

- 매개변수의 가변 인자 선언과 호출

class Varargs {
    public static void showAll(String... vargs) {
        System.out.println("LEN: " + vargs.length);

        for(String s : vargs) 
            System.out.print(s + '\t');
        System.out.println();
    }

    public static void main(String[] args) {
        showAll("Box");
        showAll("Box", "Toy");
        showAll("Box", "Toy", "Apple");
    }
}

자바에서는 인자들을 받아 배열로 만들고, 그 배열의 참조값을 참조변수가 참조하도록 한다.

- 가변 인자 선언에 대한 컴파일러의 처리

매개변수의 가변 인자 선언은 자바 5에서 추가된 문법이다.
기존 코드는 다음과 같고, 이것이 컴파일러가 변환하는 형태이다.

// 컴파일러 번역 전
public static void showAll(String... vargs) {...}

public static void main(String[] args) {
    showAll("Box");
    showAll("Box", "Toy");
    showAll("Box", "Toy", "Apple");
}
// 컴파일러 번역 후
public static void showAll(String[] vargs) {...}

public static void main(String[] args) {
    showAll(new String[]{"Box"});
    showAll(new String[]{"Box", "Toy"});
    showAll(new String[]{"Box", "Toy", "Apple"});
}

25-3. 어노테이션(Annotations)

자바 컴파일러에게 메시지를 전달하는 목적의 메모

- 어노테이션의 설명 범위

어노테이션은 자바 5에서 소개되었고, 당시 소개된 어노테이션 타입 세 가지는 다음과 같다.

@Override
@Deprecated
@SuppressWarnings

- @Override

interface Viewable {
    public void showIt(String str);
}

class Viewer implements Viewable {
    @Override
    public void showIt(String str) {
        System.out.println(str);
    }
};

class AtOverride {
    public static void main(String[] args) {
        Viewable view = new Viewer();
        view.showIt("Hello Annotations");
    }
}

상위 클래스의 메소드 오버라이딩 또는 인터페이스에 선언된 추상 메소드의 구현이다.

이에 어긋난 메소드의 정의가 이뤄지면 컴파일 오류로 이어져서 개발자로 하여금 잘못된 부분을 확인할 수 있게 해준다.

interface Viewable {
    public void showIt(long num);
}

class Viewer implements Viewable {
    @Override   // 이 어노테이션이 없으면 컴파일러는 단순히 오버로딩으로 간주하고 컴파일 단계에서 오류를 뱉지 않음
    public void showIt(int num) {
        System.out.println(str);
    }
};

class AtOverride {
    public static void main(String[] args) {
        Viewable view = new Viewer();
        view.showIt(777);
    }
}

- @Deprecated

문제의 발생 소지가 있거나 개선된 기능의 다른 것으로 대체되어서 더 이상 필요 없게 되었음을 뜻하는 말로,
아직은 호환성 유지를 위해 존재하지만 이후에 사라질 수 있는 클래스 또는 메소드를 가리켜 Deprecated 되었다고 한다.

interface Viewable {
    @Deprecated
    public void showIt(String str); // 컴파일러가 경고함!

    public void brShowIt(String str);
}

class Viewer implements Viewable {
    @Override 
    public void showIt(String str) {
        System.out.println(str);
    }

    @Override
    public void brShowIt(String str) {
        System.out.println('[' + str + ']');
    }
}

class AtDeprecated {
    public static void main(String[] args) {
        Viewable view = new Viewer();
        view.showIt("Hello Annotations");   // 컴파일러가 경고함!
        view.brShowIt("Hello Annotations");
    }
}

- @SuppressWarnings

컴파일러가 경고 메시지를 전달하는 특정 상황에 대해서, 경고 메시지를 전달하지 말라고 요청할 때 어노테이션 타입 @SuppressWarnings를 사용한다.

interface Viewable {
    @Deprecated
    public void showIt(String str);

    public void brShowIt(String str);
}

class Viewer implements Viewable {
    @Override 
    @SuppressWarnings("deprecation")    // 컴파일러의 경고 무시
    public void showIt(String str) {
        System.out.println(str);
    }

    @Override
    public void brShowIt(String str) {
        System.out.println('[' + str + ']');
    }
};

class AtSuppressWarnings {
    @SuppressWarnings("deprecation")    // 컴파일러의 경고 무시
    public static void main(String[] args) {
        Viewable view = new Viewer();     
        view.showIt("Hello Annotations");
        view.brShowIt("Hello Annotations");
    }
}

그리고 다음과 같이 동시에 여러 경고 유형에 대해 @SuppressWarnings 선언을 할 수도 있다.

@SuppressWarnings({"fallthrough", "deprecation"})

Java lang 카테고리 내 다른 글 보러가기

댓글 남기기