티스토리 뷰

포스팅에 앞서 내용이 틀릴 수 있습니다.
해당 부분 지적 감사히 받습니다.

 

이번에는 타입 안전 열거형 + Enum에 대해 알아보자.

 

자바에서는 if문을 사용하거나 switch문을 사용하여 케이스 별로 처리할 일이 정말 많다.

 

하지만 이곳에 숫자의 범위가 아닌 문자열을 통해 케이스를 나누는 경우도 존재할 것이다.

 

하지만 개발자가 개발 중 문자에 오타를 내, if문을 정상적으로 타지 않게 될 경우가 있을 수 있다.

 

물론 꼼꼼히 개발하면 일어나지 않을 문제 이긴 하지만, 그래도 사람이기에 실수가능성을 배제할 순 없다.

 

이런 문제를 컴파일 단에서 점검하고, 방지하기 위해 자바는 Enum Class를 제공한다.

 

그렇다면 Enum Class의 역할이 무엇일지 소스코드를 보며 먼저 생각해 보자.

public class Grade {
    public static final Grade BRONZE  = new Grade();
    public static final Grade SILVER  = new Grade();
    public static final Grade GOLD  = new Grade();
    public static final Grade DIAMOND  = new Grade();
    
    private Grade(){
    }
}

 

위 코드는 실제 Enum 클래스가 아니다.

 

자바에서 제공하는 Enum 클래스는 개발자가 편하게 사용하게 하기 위해 위와 같은 코드를 생략하고, 아주 간단히 쓸 수 있게 해 준다.

 

아래 코드가 실제 자바에서 제공하는 Enum 클래스이며, 위 코드와 기능이 거의 같다.

public enum Grade2 {
    BRONZE, SILVER, GOLD, DIAMOND
}

 

public static final을 사용하여 상수로 값을 지정했다.

 

타입은 Grade로 Class 객체를 담는 변수이며, new Grade()를 통해 객체 생성 후 참조값을 보관한다.

 

클래스 자체는 public이라 외부에서 접근하여 사용이 가능하지만, 생성자에 private을 붙여 Enum 클래스 외부에선 새로운 Grade 객체의 인스턴스 생성이 불가능하다.

 

따라서 Enum 클래스 내부에서 선언된 상수값만 참조하여 사용할 수 있다.

public class Main2 {
        public int discount(Grade classGrade, int price) {
            int discountPercent = 0;
            if (classGrade == Grade.BRONZE) {
                discountPercent = 10;
            } else if (classGrade == Grade.GOLD) {
                discountPercent = 20;
            } else if (classGrade == Grade.DIAMOND) {
                discountPercent = 30;
            } else {
                System.out.println("할인X");
            }
            return price * discountPercent / 100;
        }
}

매개변수로 참조값을 받아 값을 처리하는 외부 메서드이다.

 

위와 같이 사용하면 개발자가 조건에 오타를 내도 컴파일 단에서 오류를 잡아준다.

 

마찬가지로 없는 상수값을 참조하려 해도 컴파일 오류를 내준다.

 

이러면 Runtime 에러를 방지할 수 있어 잠재적인 버그차단에 도움이 된다.

 

이러한 Enum의 장점은 바로 타입 안정성에 해당한다.

 

 

Enum 클래스의 장점을 모두 알아보자.

 

 - 타입 안정성 향상 : 사전정의된 상수만 사용가능

 

 - 간결성 및 일관성 : 개발자가 코드를 읽고 해석하기 쉬워짐

 

 - 확장성 : 새로운 조건발생 시 Enum 클래스와 if문에서만 추가하면 됨

 

추가로 import static을 사용하여 Grade. 을 제거하여 상수가 담긴 변수이름만 그대로 사용할 수 있다.

 

이는 다른 개발자가 더욱 코드를 쉽게 해석하는데 도움을 준다.

import static middle.clazz.Grade.*;
+++++++++++++++++++++++++++++++++++
if (classGrade == BRONZE) {
discountPercent = 10;
}

 

 

Enum 클래스는 기본적으로 java.lang.Enum을 상속받고 있다.

 

이 Enum클래스 내부에 추상 클래스를 사용할 수도 있다. ( 익명 클래스와 방법이 같은데, 추후 다룰 예정)

 

Enum클래스는 toString()이 오버라이딩 되어있어 참조값을 확인할 수가 없다.

 

참조값을 확인하고 싶으면 아래 코드를 사용하면 된다.

Integer.toHexString(System.identityHashCode(Grade.BRONZE));

 

또한 기본 메서드들도 제공된다.

 

간단하게 이름과 기능에 대해서 알아보자

 

values() : 모든 Enum상수 배열 반환

valueOf() : 주어진 이름과 일치하는 Enum 상수 반환

name() : Enum 상수이름 문자열로 반환

ordinal() : 상수 선언순서 반환

toString() : 문자열 반환( 오버라이딩 가능하며 name()과 비슷하게 쓰인다)

 

추가로 리팩토링까지 진행 완료된 소스코드를 보며 이해하는 시간을 가져보자.

 

Main 클래스

public class Main {

    public static void main(String[] args) {
        int price = 10000;

        for (Grade g : Grade.values()) {
            printDiscount(g,price);
        }
    }

    private  static void printDiscount(Grade grade, int price){
        System.out.println(grade.name() + "등급의 할인 금액 : " + grade.discount(price));
    }
}

 

Grade(Enum) 클래스

public enum Grade {
    BASIC(10), GOLD(20), DIAMOND(30), VIP(40);

    private final int discountPercent;

    Grade(int discountPercent) {
        this.discountPercent = discountPercent;
    }

    public int discount(int price) {
        return this.discountPercent * price / 100;
    }
}

 

valueOf("문자열") 메서드는 주어진 문자열과 동일한 이름을 가진 열거형 상수의 참조값을 반환한다.

 

중요하니 잘 알아두자.

 

문제풀이 3번

 

최대한 리팩토링 해봤다.

 

열거형 클래스

public enum AuthentificationGrade {
    GUEST(1,"손님"),LOGIN(2,"로그인 회원"),ADMIN(3, "관리자");

    public final int level ;
    public final String description ;
    private AuthentificationGrade(int level, String description){
        this.level = level;
        this.description = description;
    }

    public void getMo(){
        System.out.println("당신의 등급은 " + description + "입니다.");
        getMo2(level);
    }

    private void getMo2(int level){
        System.out.println(" == 메뉴 목록 == ");

        if(level > 0){
            System.out.println(" - 메인 화면");
            if (level > 1) {
                System.out.println(" - 이메일 관리 화면");
                if (level > 2) {
                    System.out.println(" - 관리자 화면");
                }
            }
        }
    }
}

 

메인 클래스

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {

        Scanner s = new Scanner(System.in);
        System.out.print("당신의 등급을 입력하세요 [GUEST, LOGIN, ADMIN]: ");
        String inputGrade = s.nextLine().toUpperCase();
        AuthentificationGrade auth = AuthentificationGrade.valueOf(inputGrade);

        auth.getMo();

    }
}

 

와 근데 리팩토링 자바한테 맡겨보니까 메인 클래스를 두줄로 줄여주더라..

 

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        System.out.print("당신의 등급을 입력하세요 [GUEST, LOGIN, ADMIN]: ");
        AuthentificationGrade.valueOf(new Scanner(System.in).nextLine().toUpperCase()).getMo();
    }
}

 

이해는 된다.. ㅋㅋ

 

코드 유지보수를 위해 저렇게 까진 하지 말자.

 

보기 귀찮다

댓글