본문 바로가기
programming_kr/java

열거형(enum)

by JSsunday 2022. 10. 29.
728x90

 

class Direction {
	static final Direction EAST = new Direction("EAST");
    static final Direction SOUTH = new Direction("SOUTH");
    static final Direction WEST = new Direction("WEST");
    static final Direction NORTH = new Direction("NORTH");
    
    private String name;
    
    private Direction(String name){
    	this.name = name;
    }
}

열거형은 JDK1.5부터 새로 추가되었습니다.

JAVA의 열거형은 C언어의 열거형보다 향상되었고 타입까지 관리하기 때문에 논리적인 오류를 줄일 수 있습니다.

 

class {
	enum Kind {CLOVER, HEART, DIAMOND}
	enum Value {ONE, TWO}
    
	final Kind kind;
	final Value value;
}

 

1. 열거형의 정의와 사용

열거형의 정의하는 방법은 다음과 같습니다.

enum 열거형 이름 {상수1, 상수2}

 

과일 종류로 정의하는 열거형은 아래와 같습니다.

enum Fruit { APPLE, BANANA, ORANGE }

 

열거형에 정의된 상수를 사용하는 방법은 "열거형 이름.상수명"입니다.

class Shoppingback{
	Fruit fruit;
    
    void init(){
    	fruit = Fruit.APPLE;
    }
}

 

열거형 상수간의 비교는 "=="를 사용할 수 있습니다. equals()가 아닌 "=="로 비교가 가능하다는 것은 빠른 성능을 제공한다는 의미입니다. "<", ">"와 같은 비교 연산자는 사용할 수 없고 "compareTo"는 사용이 가능합니다.

if(fruit==Fruit.APPLE){}

 

switch문의 조건식에서는 열거형을 사용할 수있지만 상수의 이름만 넣어야 합니다.

switch(fruit){
	case 'APPLE' : break;
	case 'BANANA' : break;
	//...
}

 

enum에 정의된 모든 상수를 출력하려면 아래와 같습니다.

Fruit[] fruitArr = Fruit.values();
		
for(Fruit f : fruitArr) {
    System.out.printf("%s=%d%n", f.name(), f.ordinal());
}

 

values()는 열거형의 모든 상수를 배열에 담아 반환합니다. 이 메서드는 모든 열거형이 가지고 있는 것으로 컴파일러가 자동으로 추가해줍니다. ordinal()은 모든 열거형의 조상인 java.lang.Enum클래스에 정의된 것으로, 열거형 상수가 정의된 순서(0부터)를 상수로 반환합니다.

Enum 클래스에는 이외에도 아래와 같은 메서드가 정의되어 있습니다.

메서드 설명
Class<E> getDeclaringClass() 열거형의 Class 객체를 반환합니다.
String name() 열거형 상수의 이름을 문자열로 반환합니다.
int ordinal() 열거형의 상수가 정의된 순서를 반환합니다.(0부터)
T valueOf(Class<T> enumType, String name) 지정된 열거형에서 name과 일치하는 열거형 상수를 반환합니다.

 

이 외에

static E valueOf(String name)

위 메서드가 있는데 이 메서드는 열거형 상수의 이름으로 문자열 상수에 대한 참조를 얻을 수 있게 해줍니다.

System.out.println(Fruit.BANANA == Fruit.valueOf("BANANA")); //true

 

2. 열거형에 멤버 추가하기

Enum 클래스에 정의된 ordinal()이 열거형 상수가 정의된 순서를 반환하지만, 이 값은 내부적인 용도로 사용하기 때문에 사용하지 않는 것이 좋습니다. 얼거형 상수의 값이 불규칙적인 경우에 열거형 상수 이름 옆에 원하는 값을 괄호와 함께 사용할 수 있습니다.

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

 

그리고 지정된 값을 저장할 수 있는 인스턴스 변수와 생성자를 새로 추가해야 합니다. 먼저 열거형 상수를 모두 정의한 다음에 다른 멤버변수를 추가해야 합니다.

 

enum Direction {
	EAST(1), SOUTH(5), WEST(-1), NORTH(10);
	private final int value;
	Direction(int value){
		this.value = value;
	}
	public int getValue() {return value; };
};

외부에서 이 값을 얻기 위해 getValue()를 추가했습니다.

 

열거형 Direction에 새로운 생성자가 추가되었지만, 아래와 같이 열거형의 객체를 생성할 수 없는 이유는 열거형의 생성자는 묵시적으로 private 이기 때문입니다.

Direction d = new Direction(1); //error!!

 

하나의 열거형 상수에 여러 개의 값을 지정할 수 있습니다. 그에 맞게 인스턴스 변수와 생성자 등을 새로 추가해야 합니다.

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

 

3. 열거형에 추상 메서드 추가하기

열거형 Transportation은 운송 수단의 종류 별로 상수를 정의하고 있으며, 각 운송 수단에는 기본 요금(BASIC_FARE)이 책정되어 있습니다.

enum Transportation {
	BUS(100),
	TRAIN(150),
	SHIP(100),
	AIRPLANE(300);
	
	private static final BASIC_FARE;
	
	private Transportation(int basicFare) {
		BASIC_FARE = basicFare;
	}
	
	int fare() {
		return BASIC_FARE;
	}
	
}

 

거리에 따라 요금을 계산하는 방식이 각 운송 수단마다  다르기 때문에 열거형에 추상 메서드 'fare(int distance)'를 선언하면 열거형 상수가 이 추상 메서드를 구현해야 합니다.

enum Transportation {
	BUS(100) {
		@Override
		int fare(int distance) {
			// TODO Auto-generated method stub
			return distance * BASIC_FARE;
		}
	},
	TRAIN(150) {
		@Override
		int fare(int distance) {
			// TODO Auto-generated method stub
			return distance * BASIC_FARE;
		}
	},
	SHIP(100) {
		@Override
		int fare(int distance) {
			// TODO Auto-generated method stub
			return distance * BASIC_FARE;
		}
	},
	AIRPLANE(300) {
		@Override
		int fare(int distance) {
			// TODO Auto-generated method stub
			return distance * BASIC_FARE;
		}
	};
	
	abstract int fare(int distance);
	
	protected final int BASIC_FARE;
	
	private Transportation(int basicFare) {
		BASIC_FARE = basicFare;
	}
	
	public int getBasicFare() {
		return BASIC_FARE;
	}
	
}

 

4. 열거형의 이해

열거형의 이해를 돕기 위해 열거형이 내부적으로 어떻게 구현되어 있는지 알아봅시다.

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

 

열거형 하나하나가 Direction 객체입니다. 위의 문장을 클래스로 정의한다면 아래와 같습니다.

class Direction {
	static final Direction EAST = new Direction("EAST");
	static final Direction SOUTH = new Direction("SOUTH");
	static final Direction WEST = new Direction("WEST");
	static final Direction NORTH = new Direction("NORTH");
    
	private String name;
    
	private Direction(String name){
		this.name = name;
	}
}

 

Direction 클래스의 static 상수 EAST, SOUTH, WEST, NORTH의 값은 객체의 주소이고, 이 값은 바뀌지 않기 때문에 "=="로 비교가 가능합니다.

모든 열거형의 추상 클래스 Enum의 자손이므로, Enum을 흉내 내서 MyEnum을 작성하면 아래와 같습니다.

abstract Class MyEnum<T extends MyEnum<T>> implements Comparable<T>{

	static int id = 0;
    
	int ordinal;
	String name = "";
    
	public int ordinal(){ return ordinal; };
    
	MyEnum(String name){
		this.name = name;
		ordinal = id++;
	}
    
	public int compareTo(T t){
		return ordinal - t.ordinal();
	}
}

 

위에서 배운 것과 같이 객체가 생성될 때 마다 번호를 붙여서 인스턴스변수 ordinal에 저장하고 Comparable 인터페이스를 구현해서 열거형 상수간의 비교가 가능하도록 되어있습니다. 두 열거형 상수의 ordinal값을 서로 빼주기만 하면 되는데 클래스를 MyEnum<T>와 같이 선언하였다면, compareTo()를 위와 같이 작성할 수 없었을 것입니다. 타입 T에 ordinal()이 정의되어 있는지 확인할 수 없기 때문입니다.

abstract class MyEnum<T> implements Comparable<T>{
	...
    public int compareTo(T t){
    	return ordinal - t.ordinal(); //Error 타입 T에 ordinal()이 있는지 모른다.
    }
}

 

MyEnum<T extends<MyEnum<T>>와 같이 선언했으며, 이것은 타입 T가 MyEnum<T>의 자손이어야 한다는 의미입니다. 타입 T가 MyEnum의 자손이므로 ordinal()이 정의되어 있는 것은 분명하기 때문에 형변환 없이 에러가 나지 않습니다.

그리고 추상 메서드를 새로 추가하면, 클래스 앞에도 "abstract"를 붙여줘야 하고, 각 static 상수들도 추상 메서드를 구현해주어야 한다.

아래는 지금까지 배운 것들의 예시입니다.

package com.example;

public class EnumTest4 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		MyTransportation t1 = MyTransportation.BUS;
		MyTransportation t2 = MyTransportation.BUS;
		MyTransportation t3 = MyTransportation.TRAIN;
		MyTransportation t4 = MyTransportation.SHIP;
		MyTransportation t5 = MyTransportation.AIRPLANE;
		
		System.out.printf("t1=%s, %d%n", t1.name(), t1.ordinal());
		System.out.printf("t2=%s, %d%n", t2.name(), t2.ordinal());
		System.out.printf("t3=%s, %d%n", t3.name(), t3.ordinal());
		System.out.printf("t4=%s, %d%n", t4.name(), t4.ordinal());
		System.out.printf("t5=%s, %d%n", t5.name(), t5.ordinal());
		System.out.println("t1==t2 ? " + (t1==t2));
		System.out.println("t1.compareTo(t3)=" + t1.compareTo(t3));
	}

}

abstract class MyEnum2<T extends MyEnum2<T>> implements Comparable<T>{
	static int id = 0;
	int ordinal;
	String name = "";
	
	public int ordinal(){ return ordinal; };
	
	MyEnum2(String name){
		this.name = name;
		ordinal = id++;
	}
	
	public int compareTo(T t) {
		return ordinal - t.ordinal();
	}
}

abstract class MyTransportation extends MyEnum2{
	static final MyTransportation BUS = new MyTransportation("BUS", 100) {
		int fare(int distance){ return distance * BASIC_FARE; }
	};
	static final MyTransportation TRAIN = new MyTransportation("TRAIN", 100) {
		int fare(int distance){ return distance * BASIC_FARE; }
	};
	static final MyTransportation SHIP = new MyTransportation("SHIP", 100) {
		int fare(int distance){ return distance * BASIC_FARE; }
	};
	static final MyTransportation AIRPLANE = new MyTransportation("AIRPLANE", 100) {
		int fare(int distance){ return distance * BASIC_FARE; }
	};
	
	abstract int fare(int distance);
	
	protected final int BASIC_FARE;
	
	private MyTransportation(String name, int basicFare) {
		super(name);
		BASIC_FARE = basicFare;
	}
	
	public String name() { return name; }
	public String toString() { return name; }
	
}

 

참조
자바의 정석
728x90

'programming_kr > java' 카테고리의 다른 글

JDBC, DBCP 그리고 JNDI  (0) 2022.12.09
JAVA 8 함수형 인터페이스  (0) 2022.11.12
JAVA8 STREAM(1)  (1) 2022.10.15
제네릭(generic)  (1) 2022.09.24
JAVA8 Optional 사용법  (1) 2022.09.17

댓글