티스토리 뷰

한화시스템/백엔드

[BE] JAVA_ENUM

jjam-mo 2024. 7. 24. 18:49

1. 열거타입(enum)이란?

1-1. 열거타입 정의

💡 관련이 있는 상수의 집합의 클래스.
각각의 열거 상수는 열거 객체로 생성된다.
  • 상수란?
    • 상수는 변하지 않는 수이다. 예를 들어 하루는 24시간이고 한 시간은 60분이다. 이런 변하지 않는 것을 상수라고 하고 자바에서는 final 이라는 키워드로 선언한다.
    • 상수는 값이 한번 정해지면 변경할 수 없다.
    • 변수와 다르게 상수명은 모든 문자를 영어 대문자로 작성해야한다.

2. enum 의 등장 배경

2-1. 정수 열거 패턴 단점

  1. 정수 열거 패턴은 타입 안전을 보장할 방법이 없으며 에러 디버깅이 어렵다.
  2. 자바에서 별도의 namespace를 지원하지 않아 접두어를 붙여 상수 명을 구분하였어도 실제 프로그램이 기동할 때에 MEAL_AN_BUTTER_KIMCHI_STEW 와 DRINK_RADISH_KIMCHI_LATTE 의 값이 둘 다 0이기 때문에 구분하지 못한다.
  3. 정수형 상수는 문자열로 출력하기 어렵다.
  4. 정수형으로 선언된 상수명을 문자열을 출력하고 싶을 때 출력하기 어렵다.
  5. 정수 열거 그룹에 속한 모든 상수를 순회하는 방법도 쉽지 않다.
  6. 정수 열거 패턴으로 상수가 선언된 경우 상수에 변경이 발생하면 컴파일을 새로 해야한다.
  7. 정수 열거 패턴의 경우 해당 값이 클라이언트에 새겨지기 때문에 값이 변경되는 경우 다시 컴파일을 해야한다.
package com.ohgiraffers.section01.intenum;

import java.util.Scanner;

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

        /* 수업목표. 정수 열거 패턴과 이의 단점을 이해할 수 있다.(enum 아닐 떄) */
        int subject1 = Subjects.JAVA;   // 0
        int subject2 = Subjects.HTML;   // 0

        /* 설명. 1. 둘 다 같은 상수이자 숫자일 뿐 구분할 수 없다.(런타임 시점) */
        if (subject1 == subject2) {
            System.out.println("두 과목은 같은 과목이다.");
        }

        /* 설명. 2. 이름 충돌 방지를 위해서는 접두어를 써서 구분해야만 한다.(이름과 번호가 같을 때) */
        /* 설명. 3. 변수명에 쓰인 이름(문자열을 출력하기 어렵다.(feat. switch)) */
        Scanner sc = new Scanner(System.in);
        System.out.print("과목 번호를 눌러주세요(0 ~ 2): ");
        int fieldNo = sc.nextInt();

        String subName = "";
        switch (fieldNo) {
            case Subjects.JAVA: subName = "Java"; break;
            case Subjects.MARIADB: subName = "MariaDB"; break;
            case Subjects.JDBC: subName = "JDBC"; break;
        }

        System.out.println("선택한 과목명은: " + subName + "입니다.");

        /* 설명. 4. 같은 클래스에 속한 상수들을 순회(반복자/반복문)할 수 없다.(필드가 총 몇 개고 어떤 필드들이 있는지) */ // ???????????????????????
        /* 설명. 5. 타입 안정성을 보장할 수 없다. */
        printSubject(Subjects.JAVA);
        printSubject(0);            // 그냥 숫자의 개념도 허용이 된다.
    }
    /* 설명. 매개변수의 의미는 단순히 정수가 넘어온다 밖에 없다.(Subjects라는 개념이 문법적으로 적용되지 X) */
    private static void printSubject(int subjectName) {
    }
}
package com.ohgiraffers.section01.intenum;

public interface Subjects {
    // 열거 패턴에 입각한 상수값(final) 나열
    public static final int JAVA = 0;
    public static final int MARIADB = 1;
    public static final int JDBC = 2;

    public static final int HTML = 0;
    public static final int CSS = 1;
    public static final int JAVASCRIPT = 2;
}

 

3. 열거 타입의 등장

3-1. 열거타입 선언 및 사용 방법

접근제어자 enum 열거체이름 {

	상수명1, 상수명2, ... 상수명XX;		
		
}

 

3-2. 열거 타입 장점

  1. 열거 타입은 싱글톤 방식으로 구현되기 때문에 인스턴스의 생성이나 확장이 되지 않는다. 즉, 인스턴스가 통제되며 하나만 존재하는 것이 보장된다.
  2. 열거 타입은 컴파일 타임 타입 안정성을 높여준다. enumTest() 는 FoodsEnum 타입으로 받도록 선언해 두었기 때문에 다른 타입의 매개변수가 들어오는 경우 컴파일 에러를 발생시켜 준다.
  3. 열거 타입은 상수 이름을 문자열로 출력할 수 있다.
  4. 열거 타입에 메소드나 필드를 추가 할 수 있다. 추가로 열거 타입은 근본적으로 불변이라 모든 필드는 final 이어야 한다
  5. 열거 타입도 클래스이기 때문에 toString() 을 재정의하여 사용할 수 있다.
  6. 열거 타입에 선언된 상수 하나를 제거하더라도 클라이언트에 영향을 주지 않는다. 혹시나 삭제된 상수를 참조하고 있는 곳이 있다면 컴파일 에러를 출력해주기 때문에 안전하다.
  7. 열거 타입 내에 선언된 상수들을 순회할 수 있다.
package com.ohgiraffers.section02.enumtype;

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

        /* 수업목표. 열거형(enum)을 이용하여 열거 패턴의 단점을 해결하는 것을 이해할 수 있다. */
        Subjects subject1 = Subjects.JAVA;
        Subjects subject2 = Subjects.HTML;      // 객체 새로 안만들고 만들었던 것을 이용한다. 즉, singleton
        Subjects subject3 = Subjects.JAVA;

        /* 설명.
        *   1. 열거 타입으로 선언된 인스턴스는 싱글톤으로 관리되며 인스턴스가 각각 한 개임을 보장한다.
        *      작성한 순서에 따라 각각은 다른 인스턴스가 생성되며 최초 호출 시에 enum의 생성자를 활용해
        *      생성된다.(lazy singleton 개념)
        * */
        if(subject1 == subject2) {
            System.out.println("두 과목은 같은 과목이다.");
        } else {
            System.out.println("서로 다른 과목이다.");
        }

        /* 설명. 2. 단일 인스턴스임을 보장하기에 == 비교가 가능하다.(동일 객체 비교가 가능하다.) */
        if(subject1 == subject3) {
            System.out.println("싱글톤이다.");
        }

        /* 설명. 3. 오버라이딩 되지 않은 toString() 또는 name() 메소드를 활용하여 필드명을 문자열로 변경하기 쉽다.(feat. switch 안씀) */
        // 개발할 때 썼던 단어 그대로 사용할 수 있다.
        System.out.println(Subjects.JAVA.toString());       // 내가 재정의 할 수 있다.
        System.out.println(Subjects.JAVA.name());

        /* 설명. 4. values()를 이용하여 상수값 배열을 반환받고 이를 활용하여 연속처리를 해 줄 수 있다. */
        Subjects[] subjects = Subjects.values();
        for (Subjects sub : subjects) {
            System.out.println(sub.toString());
            System.out.println(sub.ordinal());      // 인덱스 값으로 먼저 생성된 것을 알려준다.
            System.out.println(sub.name());
        }

        /* 설명. 5. 타입 안정성을 보장한다. */
        printSubjects(Subjects.JAVA);
//        printSubjects(0);               // Subjects 타입이 아니라서 에러 발생
    }

    private static void printSubjects(Subjects subjects) {
    }
}

// 실행 결과
기본 생성자 호출됨...
기본 생성자 호출됨...
기본 생성자 호출됨...
기본 생성자 호출됨...
기본 생성자 호출됨...
기본 생성자 호출됨...

서로 다른 과목이다.

싱글톤이다.

@@@@JAVA@@@@
JAVA

@@@@JAVA@@@@
0
JAVA
@@@@MARIADB@@@@
1
MARIADB
@@@@JDBC@@@@
2
JDBC
@@@@HTML@@@@
3
HTML
@@@@CSS@@@@
4
CSS
@@@@JAVASCRIPT@@@@
5
JAVASCRIPT
package com.ohgiraffers.section02.enumtype;

public enum Subjects {
    JAVA,           // 0    / = new Subjects(); / Lazy Singleton??????????????????
    MARIADB,        // 1
    JDBC,           // 2
    HTML,           // 3
    CSS,            // 4
    JAVASCRIPT;     // 5    / 나열 다 끝나면 세미콜론(;) 찍자!

    Subjects() {
        System.out.println("기본 생성자 호출됨...");
    }

    public String toString() {
        return "@@@@" + this.name() + "@@@@";
    }

}

4. 열거 타입(enum) 문법

package com.ohgiraffers.section03.grammer;

import java.util.EnumSet;
import java.util.Iterator;

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

        /* 수업목표. Enum의 문법에 대해 이해하고 활용할 수 있다.(클래스적인 면모) */
        /* 설명.
        *   enum 타입의 필드를 최초 사용 시에만 열거 타입의 인스턴스를 생성하고 생성자를
        *   따로 호출하지 않는다. 또한 enum 타입은 set으로 바꿔 반복시키며 필드들을
        *   한번에 확인하고 활용할 수 있다.
        *  */
        UserRole1 admin = UserRole1.ADMIN;
        System.out.println(admin.name());
        System.out.println(admin.getNameToLowerCase());

        UserRole2 consumer = UserRole2.CONSUMER;
        System.out.println(consumer.ordinal() + ", " + consumer.name()
                            + ", " + consumer.getDescription());    // 클래스적인 면모!!

        System.out.println("Set으로 바꿔 반복자를 활용해 보자.");
        EnumSet<UserRole2> roles = EnumSet.allOf(UserRole2.class);  // UserRole2의 타입(.class)
        Iterator<UserRole2> iter = roles.iterator();
        while (iter.hasNext()) {
            System.out.println(iter.next().name());
        }
    }
}

// 실행 결과
ADMIN
admin

1, CONSUMER, 구매자

Set으로 바꿔 반복자를 활용해 보자.
GUEST
CONSUMER
PRODUCER
ADMIN
package com.ohgiraffers.section03.grammer;

public enum UserRole1 {
    GUEST,
    CONSUMER,
    PRODUCER,
    ADMIN;

    /* 설명. 단순한 메소드 하나 추가해 보기 */
    public String getNameToLowerCase() {
        return this.name().toLowerCase();
    }
}
package com.ohgiraffers.section03.grammer;

public enum UserRole2 {
    GUEST("게스트"),
    CONSUMER("구매자"),
    PRODUCER("판매자"),
    ADMIN("관리자");

    private final String DESCRIPTION;

    UserRole2(String description) {
        DESCRIPTION = description;
    }

    public String getDescription() {
        return DESCRIPTION;
    }
}

'한화시스템 > 백엔드' 카테고리의 다른 글

[BE] Spring AOP  (0) 2024.08.12
[BE] JAVA_Stream  (0) 2024.07.29
[BE] JAVA_컬렉션(Collection)_Map  (2) 2024.07.24
[BE] JAVA_컬렉션(Collection)_Set  (1) 2024.07.24
[BE] JAVA_컬렉션(Collection)_List  (1) 2024.07.23
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함