[자바]/자바

[Java] 열거형 (enum)

쿠릉쿠릉 쾅쾅 2022. 1. 26. 16:32
728x90

 

📍 학습 목표 
열거형(enum)은 자바5에서 추가된 자료형으로 '의미가 부여된 이름'을 갖는 '상수'의 선언에 그 목적이 있다.
열거형(enum)가 추가 되기 전/후를 비교해서 어떤 점이 달라졌고 열거형이 무엇인지 알아보자!

 

 

열거형(enum) 추가 되기 전

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

interface Scale {
	int DO = 0; int RE = 1; int MI = 2; int FA = 3;
	int SO = 4; int RA = 5; int SI = 6;
}
  • 인터페이스 내에 선언된 변수는 public, static, final이 선언된 것으로 간주한다.

 

🔍 인터페이스 기반의 상수 정의의 문제점

public  class Prac {	

    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;
    		
    	}  // end of switch
    	
    } 
    
}


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

interface Person {
	int MAN = 1;
	int WOMAN = 2;
}
남성 손닙입니다.
남성 손닙입니다.
  • Person.MAN의 값이 1이다. 하지만 Animal.DOG의 값도 1이다. 그렇기 때문에 who() 메서드에 어떤 인터페이스의 상수가 오든 int 타입일 경우 컴파일 오류와 실행 오류를 발생시키지 않는다. 
  • 이런 문제를 해결하기 위해서 자바5에서 열거형(enum)이 추가됐다.

 

 

public  class Prac {	

    public static void main (String[] args) {    
    	System.out.println(Animal.CAT == Person.WOMAN);
    } 
    
}


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

interface Person {
	int MAN = 1;
	int WOMAN = 2;
}
true
  • 인터페이스 기반의 상수를 정의의 문제점을 더 자세히 보자면, Animal.CATPerson.WOMAN은 서로 값은 같지만 타입이 달라서 false 값이 나와야 하지만 인터페이스 기반의 상수 정의에서는 true가 나온다. 

 

 

열거형(enum) 추가

열거형 (enum)

  • 모든 열거형은 java.lang.Enum 클래스의 자손이다.
  • 열거형은 서로 관련된 상수를 편리하게 선언하기 위한 것으로 여러 상수를 정의할 때 사용하면 유용하다.
  • 열거형은 '타입에 안전한 열거형(typesafe enum)'이라서 실제 값이 같아도 타입이 다르면 컴파일 에러가 발생한다.
    • 인터페이스 기반의 상수 정의의 문제점 해결!
    • enum 타입은 고정된 상수들의 집합으로 런타임이 아닌 컴파일 타임에 모든 값을 알고 있어야한다.
  • 상수의 값이 바뀌면, 해당 상수를 참조하는 모든 소스를 다시 컴파일 해야한다. 하지만 열거형 상수를 사용하면, 기존의 소스를 다시 컴파일 하지 않아도 된다.
  • 열거형 상수간의 비교에는 ==, equals() 를 사용할 수 있다. 그러나 ==의 속도가 더 빠르다.
    • ==는 객체의 주소 비교이다. 열거형의 상수는 값이 바뀌지 않으므로 ==로 비교 가능한 것이다.
  • <, > 같은 비교 연산자는 사용할 수 없으나 compareTo()는 사용 가능하다.
    • compareTo()로 비교할 때, 두 열거형 상수의 ordinal() 값을 서로 뺴준다. 비교 대상이 같으면 0. 왼쪽이 크면 양수, 오른쪽이 크면 음수를 반환한다.
    • Enum 클래스는 Comparable<E>를 구현한 클래스다. 
  • 열거형의 생성자는 private만 선언할 수 있으나 생략 가능하다.
    • 열거형의 생성자는 외부에서 호출불가

 

 

🔍 열거형의 정의

💡 방법1

enum 열거형이름 {상수명A, 상수명B, ...}

 

💡 방법2

enum 열거형이름 {상수명A(1), 상수명B(2), ...}
  • 열거형 상수의 값이 불연속적인 경우에는 열거형 상수의 이름 옆에 원하는 값을 괄호()와 함께 적어주면 된다.
  • 지정된 값을 저장할 수 있는 인스턴스 변수와 생성자를 새로 추가해줘야 한다.
    • 이 때 열거형 상수를 먼저 정의한 다음에 열거형 상수 마지막에 ';' 를 붙인 후에 다른 멤버들을 추가해야한다.
  • 하나의 열거형 상수에 여러 값을 지정할 수도 있다. 다만 그에 맞게 인스턴스 변수와 생성자 등을 새로 추가해야 한다.
더보기
더보기

예제1

enum Direction {
	EAST(1), SOUTH(5), WEST(-1), NORTH(10);  // 끝에 ; 추가할 것
	
	private int value;
	
	Direction(int value) {
		this.value = value;
	}
}

 

예제2

enum Direction {
	EAST(1, ">"), SOUTH(5, "V"), WEST(-1, "<"), NORTH(10, "^");
	
	private int value;
	private String symbol;
	
	Direction(int value, String symbol) {
		this.value = value;
		this.symbol = symbol;
	}
}

 

 

🔍 Enum 클래스의 메서드

메서드 설명
Class<E> getDeclaringClass() 열거형의 Class객체를 반환
String name() 열거형 상수의 이름을 문자열로 반환
int ordinal() 열거형 상수가 정의된 순서를 반환 (0부터 시작)
static <T extends Enum<T>> valueOf(Class<T>, enumType,  String name) 지정된 열거형에서 name과 일치하는 열거형 상수를 반환
int compareTo(E o) 비교한다.
  • ordinal()이 열거형 상수에 정의된 순서를 반환하지만, 이 값을 열거형 상수의 값으로 사용하지 않는 것이 좋다. 이 값은 내부적인 용도로만 사용되기 위한 것이기 때문이다.

 

💡 컴파일러가 자동으로 추가해주는 메서드

메서드 설명
static E[] values() 열거형의 모든 상수를 배열에 담아 반환
static E valueOf(String name) 열거형 상수의 이름으로 문자열 상수에 대한 참조를 얻을 수 있다.

 

더보기
더보기
public  class Prac {	

    public static void main (String[] args) {    
 
    	Direction d = Direction.valueOf("WEST");
    	
    	System.out.println(d);
    	System.out.println(Direction.WEST == Direction.valueOf("WEST"));
    	
    }
}

enum Direction {
EAST, SOUTH, WEST, NORTH
}
WEST
true

 

 

🔍 예제

예제1

public  class Prac {	

    public static void main (String[] args) {    
    	Direction d1 = Direction.EAST;
    	Direction d2 = Direction.valueOf("WEST");
    	Direction d3 = Enum.valueOf(Direction.class, "EAST");
    	
    	System.out.println("d1 = " + d1);
    	System.out.println("d2 = " + d2);
    	System.out.println("d3 = " + d3);
    	
    	System.out.println();
    	
    	System.out.println("d1 == d2 ? " + (d1==d2));
    	System.out.println("d1 == d3 ? " + (d1==d3));
    	System.out.println("d1.equals(d3) ? " + d1.equals(d3));
    	System.out.println("d1.compareTo(d3) ? " + (d1.compareTo(d3)));
    	System.out.println("d1.compareTo(d2) ? " + (d1.compareTo(d2)));
    	
    	System.out.println();
    	
    	move(d1);
    	
    	System.out.println();
    	
    	Direction[] dArr = Direction.values();
    	
    	for(Direction d: dArr)
    		System.out.printf("%s = %d%n", d.name(), d.ordinal());
    	
    } 
    
    static void move(Direction dir) {
    	switch(dir) {
    	case EAST:
    		System.out.println("EAST");  // Direction.EAST라고 쓰면 안된다.
    		break;
    	case SOUTH:
    		System.out.println("SOUTH");
    		break;
    		
    	case WEST:
    		System.out.println("WEST");
    		break;
    		
    	case NORTH:
    		System.out.println("NORTH");
    		break;
    		
    	default:
    		System.out.println("Invalid diraction");
    	}  // end of switch
    	
    	
    }
    
}

enum Direction {
EAST, SOUTH, WEST, NORTH
}
d1 = EAST
d2 = WEST
d3 = EAST

d1 == d2 ? false
d1 == d3 ? true
d1.equals(d3) ? true
d1.compareTo(d3) ? 0
d1.compareTo(d2) ? -2

EAST

EAST = 0
SOUTH = 1
WEST = 2
NORTH = 3
  • swtich문의 조건식에도 열거형을 사용할 수 있다.
  • 주의할 점은 case문에 열거형의 이름은 적지 않고 상수의 이름만 적어줘야한다.

 

 

예제2

public  class Prac {	

    public static void main (String[] args) {    
    	
    	for(Direction d : Direction.values())
    		System.out.printf("%s = %d%n", d.name(), d.getValue());
    	
    	Direction d1 = Direction.EAST;
    	Direction d2 = Direction.of(1);
    	
    	System.out.println();
    	
    	System.out.printf("d1 = %s, %d%n", d1.name(), d1.getValue());
    	System.out.printf("d2 = %s, %d%n", d2.name(), d2.getValue());
    	
    	System.out.println(Direction.EAST.rotate(1));
    	System.out.println(Direction.EAST.rotate(2));
    	System.out.println(Direction.EAST.rotate(-1));
    	System.out.println(Direction.EAST.rotate(-2));
    	
    }
    
}

enum Direction {
	EAST(1, ">"), SOUTH(5, "V"), WEST(-1, "<"), NORTH(10, "^");
	
	private static final Direction[] DIR_ARR = Direction.values();
	private int value;
	private String symbol;
	
	Direction(int value, String symbol) {
		this.value = value;
		this.symbol = symbol;
	}
	
	public int getValue() {return value;}
	public String getSymbol() {return symbol;}

	public static Direction of(int dir) {
		
		// 0~3 범위 벗어나면 예외 발생
		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();
	}
	
}
EAST = 1
SOUTH = 5
WEST = -1
NORTH = 10

d1 = EAST, 1
d2 = EAST, 1
SOUTHV
WEST<
NORTH^
WEST<

 

 

예제3 ㅡ 열거형에 추상 메서드 추가하기

public  class Prac {	

    public static void main (String[] args) {    
    	
    	System.out.println("bus fare = " + Transportation.BUS.fare(100));
    	System.out.println("train fare = " + Transportation.TRAIN.fare(100));
    	System.out.println("ship fare = " + Transportation.SHIP.fare(100));
    	System.out.println("airplane fare = " + Transportation.AIRPLANE.fare(100));
    	
    }
    
}

enum Transportation {
	BUS(100) {
		int fare(int distance) {
			return distance * BASIC_FARE;
		}
	},
	TRAIN(150){
		int fare(int distance) {
			return distance * BASIC_FARE+ 50;
		}
	},
	SHIP(100){
		int fare(int distance) {
			return distance * BASIC_FARE + 100;
		}
	},
	AIRPLANE(300){int fare(int distance) {return distance * BASIC_FARE * 2;}};
	
	
	protected int BASIC_FARE;  // protected로 해야 각 상수에서 접근 가능

	Transportation(int basicFare) {
		BASIC_FARE = basicFare;
	}

	public int getBasicFare() {return BASIC_FARE;}
	abstract int fare(int distance);
	

}
bus fare = 10000
train fare = 15050
ship fare = 10100
airplane fare = 60000
  • 열거형에 추상 메서드 fare(int distance)를 선언하면 각 열거형 상수가 이 주상 메서드를 반드시 구현해야 한다.
  • 각 열거형 상수가 추상 메서드 fare()를 각기 다른 내용으로 구현할 수 있어서 거리에 따라 요금을 계산하는 방식을 운송 수단마다 다르게 할 수 있다. 

 

 

728x90