[JAVA] 열거형 (Enum)

DongGyu Jung·2022년 3월 9일
0

자바(JAVA)

목록 보기
41/60
post-thumbnail

🏃‍♂️ 들어가기 앞서..

본 게시물은 스터디 활동 중에 작성한 게시물로 자바의 정석-기초편 교재를 학습하여 정리하는 글입니다.
※ 스터디 Page : 〔투 비 마스터 : 자바〕

*해당 교재의 목차 순서와 구성을 참고하여 작성하며
각 내용마다 부족할 수 있는 내용이나 개인적으로 궁금한 점은
추가적인 검색을 통해 채워나갈 예정입니다.



🚂 열거형 (enum)

여러 상수를 선언해야 할 때,
" 관련된 상수들을 같이 묶어 놓은 것 " (Java는 타입에 안전한 열거형 제공)

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;
    
    final int kind;
    final int num;
}

// 열거형으로 선언
class Card {//	0		1		2		3
	enum Kind { CLOVER, HEART, DIAMOND, SPADE }
    enum Value { TWO, THREE, FOUR }
    
    final Kind kind ; // 타입 int 아님 주의!! Kind 타입
    final Value value ;
}

여러가지 상수를 선언할 때
번거로운 선언 작업들을 enum을 통해 간단하게 선언할 수 있다.

처음 코드처럼 따로 값을 지정해주지 않아도
자동적으로 0부터 시작하는 정수값이 할당 된다.

또한
해당 상수들을 사용하는 경우에서도
활용하고자 하는 의도에 맞게 타입이 분류되어야 하는데

처음 코드를 적용했을 경우에는
Card.CLOVER == Card.TWO 와 같은 논리연산자를 수행하게 되면
true가 반환되게 된다.
하지만 쓰고자 하는 의도대로라면 false여야하는데
의미가 어긋나게 되는 것이다.

이런 경우
enum은 간편한 선언뿐만 아니라

열거형을 이용해 선언한 상수들의 비교는
" 값 비교에 앞서 타입 비교를 선행 "하기 때문에

값이 같을 수도 있는 상수들간에 분류를 할 수 있다.

▶ 아래 열거형으로 선언한 상수들에 대해
Card.CLOVER == Card.TWO 와 같은 논리연산자를 수행하게 되면
타입이 다르기 때문에 컴파일 에러가 발생게 된다.


🔧 정의 & 사용

괄호{} 안에 상수의 이름을 나열

enum (열거형 이름) { 상수명 1, 상수명 2, ... }
  • 사용 방법 : (열거형 이름).(상수명)

사용 방법은 .을 통해서 각 열거형들에 속해있는 상수들을 사용할 수 있고

상수간의 비교에 compareTo()와 더불어 ==도 사용할 수 있다.
==연산자가 사용 가능하다는 것은 함수를 통해 비교보다
더 빠른 성능의 비교가 가능해지는 것이다.

  • == 연산자 : true / false 반환
  • 차이 값 반환 : compareTo() 사용 _ 왼쪽이 크면 양수 / 오른쪽이 크면 음수

하지만 <> 비교연산자는 사용할 수 없다는 점 주의하자.
( 열거형에서 각 상수들은 객체로서 취급되기 때문에 해당 비교연산자들이 적용되지 않는다. )



🔗 열거형의 조상 - java.lang.Enum

※ 모든 열거형 → Enum 자손

Method설명
Class<E> getDeclaringClass()열거형의 Class 객체 반환
String name()열거형 " 상수의 이름 문자열로 " 반환
int ordinal()열거형 상수가 " 정의된 순서 " 반환
T valueOf( Class<T> enumType, String name )지정 열거형에서 " name과 일치하는 열거형 상수 " 반환

※ 컴파일러가 자동으로 추가

  • values() : 열거형 객체에 정의된 모든 상수 " 배열 " 반환
  • valueOf( String name ) : 열거형 상수의 이름으로 문자열 상수에 대한 참조
static E[] values()
static E valueOf( String name )

enum Direction {EAST, SOUTH, WEST, NORTH} /* 상수 하나하나가 객체로 다뤄지게 됨 */

Direction[] dArr = Direction.values() ;

for (Direction d : dArr) 
	System.out.println("%s=%d%n", d.name(), d.ordinal() ); // 열거형 내 상수들 하나씩 name메서드 & ordinal메서드


Direction d = Direction.valueOf("WEST") ;

System.out.println(d) ; // WEST 출력
System.out.println(Direction.WEST == Direction.valueOf("WEST")) ; //  상수에 대한 참조 가져와서 비교 _ true

위에서 알아본 다양한 메서드들의 쓰임 예시를 살펴보자.

우선
열거형의 상수를 얻어오는 방법에 대해 알아보자.

enum Direction {EAST, SOUTH, WEST, NORTH}
...
/* 열거형 상수 가져오기 */
Direction d1 = Direction.EAST
Direction d2 = Direction.valueOf("WEST")
// Enum은 모든 열거형의 조상으로 해당 조상클래스의 valueOf 메서드를 사용하게 되면
// 인수를 String name에 앞서 대상 객체를 지정해줘야 한다.
Direction d3 = Enum.valueOf( Direction.class, "EAST" ) ;

위와 같은 방법으로 열거형의 상수를 가져올 수 있고

다음으로
열거형 상수 간 비교하는 방법을 알아보자.

System.out.println( "d1=" + d1 ); // EAST
System.out.println( "d2=" + d2 ); // WEST
System.out.println( "d3=" + d3 ); // EAST

System.out.println( d1 == d2 ); // false
System.out.println( d1 == d3 ); // true

System.out.println( d1.equals(d3) ); // true
// System.out.println( d1 > d3 ); // 앞서 설명했듯이 각 상수는 객체로 취금 → 에러!! XXXXXX
System.out.println( d1.compareTo(d3) ); // 0
System.out.println( d1.compareTo(d2) ); // -2

/* EAST Side 출력 */
switch( d1 ) {
	case EAST : // ★ Direction.EAST 라고 쓰면 불가능 
    	System.out.println("EAST Side"); break;
    case SOUTH :
    	System.out.println("SOUTH Side"); break;
    case WEST :
    	System.out.println("WEST Side"); break;
    case NORTH :
    	System.out.println("North Side"); break;
    default :
    	System.out.println("Invalid Direction.") ; break;
}

※ 열거형 멤버 추가

앞서 살펴봤었던
Enum클래스의 정의 메서드 ordinal()
" 열거형 상수의 정의 순서 "를 반환한다고 설명했는데

이 순서 값은
자동적으로 0부터 시작하는 정수값이 할당된 것 이며
그저 내부적인 용도로만 쓰기 위한 것이기 때문에
" 열거형 상수의 값 " 으로 사용하는 것은 바람직하지 않고


《 열거형 상수의 값이 불규칙적인 경우 / 다르게 하고 싶은 경우 / 여러 값을 가지게 하고 싶을 경우 》에는

[ 첫번째 ]
상수의 이름 옆에
()와 함께 원하는 값을 적으면 된다.

enum Direction { EAST(1), SOUTH(5), WEST(-1), NORTH(10) }

[ 두번째 ]
지정된 값을 저장할 수 있는 인스턴스 변수 & 생성자
새로 추가해주어야 한다.
( 단, 먼저 열거형 상수를 모두 정의한 다음, 다른 멤버 추가작업을 수행해야한다. )

enum Direction { 
	EAST(1), SOUTH(5), WEST(-1), NORTH(10) ; // ★ ; 세미콜론 잊지 않게 주의
	
    private final int value ; // 정수 저장을 위한 인스턴스 변수 추가
    // private 생략 ← Direction 생성자
    Direction(int value) { // 생성자 추가
    	this.value = value ; // 각 상수가 가지는 값들에 대해 정의
    }
    
    public int getValue() { //외부에서도 위 값들을 얻을 수 있도록 public 메서드 정의
    	return value ;
    }
}

/* 생성자는 private 이기때문에 
외부에서 객체 생성 불가능!!! */
Direction d = new Direction(1) ; // 불가능!!!!!

주의할 점은
" 외부에서 생성자 호출 "이 불가능하다는 것 정도인 것 같다.

<더 알아보기>

enum Direction { 
	// 상수에 2개 이상의 값이 들어갈 경우
	EAST(1, ">"), SOUTH(2, "V"), WEST(3, "<"), NORTH(4, "^" ;) 
    
    // 이번엔 배열로 저장되는 내부 인스턴스 변수 추가
    private static final Direction[] DIR_ARR = Direction.values();
    
    private final int value ; // 정수 저장을 위한 인스턴스 변수 추가
    private final String value ; // 문자열 저장을 위한 인스턴스 변수 추가
    // Direction 생성자
    Direction(int value, String symbol) { // 생성자 추가
    // 각 상수가 가지는 값들에 대해 정의
    	this.value = value ; 
        this.symbol = symbol;    
    }
    
    public int getValue() { return value; } 
    public String getSymbol() { return symbol; }
    
    // 위에 만들었던 배열 인스턴스 변수를 사용하는
    // 외부에서 메서드는 쓸 수 있게끔 public 메서드 선언
    public static Direction of( int dir ) {
    	
        if (dir < 1 || dir > 4) // 위 상수들의 value 값 범위를 벗어날 경우
        	// 예외 발생시키지
        	throw new IllegalArgumentException("Invalid value :" + dir ) ;
        
        return DIR_ARR[dir -1] // 순서에 맞는 값 반환
        /* EAST 상수의 value : 1
        1 에서 -1 한 값은 0 → 실제 EAST 상수의 index와 동일*/
        
    }
}

0개의 댓글