[Java] 참조 타입 (3)
열거타입
데이터 중에는 몇 가지로 한정된 값만을 갖는 경우가 흔히 있다. 예를 들어 요일에 대한 데이터는 월, 화, 수, 목, 금, 토, 일 이라는 일곱 개의 값만을 갖고, 계절에 대한 데이터는 봄, 여름, 가을, 겨울 이라는 네 개의 값만을 가진다. 이와 같이 한정된 값만을 갖는 데이터 타입이 열거 타입(enumeration type)이다. 열거 타입은 몇 개의 열거 상수 중에서 하나의 상수를 저장하는 데이터 타입이다.
열거 타입 선언
열거 타입을 선언하기 위해서는 먼저 열거 타입의 이름을 정하고 열거 타입 이름으로 소스 파일을 생성해야 한다. 열거 타입 이름은 관례적으로 첫 문자를 대문자로 하고 나머지는 소문자로 구성한다. 만약 여러 단어로 구성된 이름이라면 단어 첫 문자는 대문자로 하는것이 관례이다. 다음은 잘 만들어진 열거 타입 소스 파일들의 이름이다.
Week.java
MemberGrade.java
ProductKind.java
소스 파일의 내용으로는 다음과 같이 열거 타입 선언이 온다.
public enum Week { // Week == "열거 타입 이름"
// 열거 상수
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY
}
public enum 키워드는 열거 타입을 선언하기 위한 키워드이다. 반드시 소문자로 작성해야 한다. 열거타입 이름은 소스 파일명과 대소문자가 모두 일치해야 한다.
열거 타입을 선언했다면 이제는 열거 상수를 선언하면 된다. 열거 상수는 열거타입의 값으로 사용되는데, 관례적으로는 열거 상수는 모두 대문자로 작성한다. 만약 열거 상수가 여러 단어로 구성될 경우에는 단어 사이를 밑줄( _ )로 연결하는 것이 관례이다. 예를 들면 다음과 같다.
public enum LoginResult { LOGIN_SUCCESS, LOGIN_FAILED }
열거 타입 변수
열거 타입을 선언했다면 이제 열거 타입을 사용할 수 있다. 열거 타입도 하나의 데티어 타입이므로 변수를 선언하고 사용해야 한다. 다음은 열거 타입 변수를 선언하는 방법이다.
열거타입 변수;
예를 들어 열거 타입 Week로 변수를 선언하면 다음과 같다.
Week today;
Week reservationDay;
열거 타입 변수를 선언했다면 다음과 같이 열거 상수를 저장할 수 있다. 열거 상수는 단독으로 사용할 수는 없고 반드시 열거타입, 열거상수로 사용된다.
열거타입 변수 = 열거타입.열거상수;
예를 들어 today 열거 변수에 열거 상수인 SUNDAY를 저장하면 다음과 같다.
Week today = Week.SUNDAY;
열거 타입 변수는 null 값을 저장할 수 있는데, 열거 타입도 참조 타입이기 때문이다.
Week bithday = null;
참조 타입 변수는 객체를 참조하는 변수라고 알고 있는데, 그렇다면 열거 상수는 객체일까? 그렇다. 열거 상수는 열거 객체로 생성된다. 열거 타입 Week의 경우 MONDAY부터 SUNDAY까지의 열거 상수는 총 7개의 Week 객체로 생성된다. 그리고 메소드 영역에 생성된 열거 상수가 해당 Week 객체를 각각 참조하게 된다.
그렇다면 다음 코드를 어떻게 이해하면 좋을까?
Week today = Week.SUNDAY;
열거 타입 변수 today는 스택 영역에 생성된다. today에 저장되는 값은 Week.SUNDAY 열거 상수가 참조하는 객체의 번지이다. 따라서 열거 상수 Week.SUNDAY와 today 변수는 서로 같은 Week 객체를 참조하게 된다. 그렇기 때문에 today 변수와 Week.SUNDAY 상수의 == 연산은 true 가 된다.
today == Week.SUNDAY // true
그렇다면 다음 코드에서 변수 week1과 week2의 == 연산의 결과가 왜 true인지 이해할 수 있을 것이다.
Week week1 = Week.SATURDAY;
Week week2 = Week.SAYURDAY;
System.out.println( week1 == week2 ); // true
week1과 week2는 모두 Week.SATURDAY 상수와 같이 동일한 Week 객체를 참조하기 때문이다.
자바는 컴퓨터의 날짜 및 요일, 시간을 프로그램에서 사용할 수 있도록 하기 위해 Date, Calendar, LocalDateTime 등의 클래스를 제공한다. LocalDateTime은 자바 8부터 지원하는 API이다. 이전 버전과의 호환성을 위해 Calendar를 이용해서 날짜와 시간을 얻는 방법을 알아보자. 우선 Calendar변수를 선언하고 Calendar.getInstance() 메소드가 리턴하는 Calendar 객체를 얻는다.
Calendar now = Calendar.getInstance();
Calendar 객체를 얻었따면 get() 메소드를 이용해서 년, 월, 일, 요일, 시간, 분, 초를 다음과 같이 얻을 수 있다.
int year = now.get(Calendar.YEAR); // 년
int month = now.get(Calendar.MONTH) + 1; // 월
int day = now.get(Calendar.Day_OF_MONTH); // 일
int week = now.get(Calendar.DAY_OF_WEEK); // 요일
int hour = now.get(Calendar.HOUR); // 시간
int minute = now.get(Calendar.MINUTE); // 분
int second = now.get(Calendar.SECOND); // 초
그럼 Calendar를 이용해서 오늘의 요일을 얻고 나서 열거 타입 변수 today에 해당 열거 상수를 대입하는 예제를 살펴보자. Calendar를 사용하기 위해서는 1라인의 import문이 필요하다. import는 나중에 설명된다. 7라인에서 Calendar를 얻고, 8라인에서 요일별 숫자를 얻는다. 10 ~ 25라인은 switch문을 이용해서 열거 타입 변수 today에 열거 상수를 대입한다. 29 ~ 33 라인은 if문을 이용해서 일요일인지 확인하고 출력할 내용을 결정한다.
public class EnumWeekExample {
public enum Week {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY
}
public static void main(String[] args) {
Week today = null;
Calendar cal = Calendar.getInstance();
int week = cal.get(Calendar.DAY_OF_WEEK);
switch(week) {
case 1:
today = Week.SUNDAY; break;
case 2:
today = Week.MONDAY; break;
case 3:
today = Week.TUESDAY; break;
case 4:
today = Week.WEDNESDAY; break;
case 5:
today = Week.THURSDAY; break;
case 6:
today = Week.FRIDAY; break;
case 7:
today = Week.SATURDAY; break;
}
System.out.println("오늘 요일 : " + today);
if(today == Week.SUNDAY) {
System.out.println("일요일에는 축구를 합니다.");
}
else {
System.out.println("열심히 자바 공부합니다.");
}
}
}
열거 객체의 메소드
열거 객체는 열거 상수의 문자열을 내부 데이터로 가지고 있다. 메소드는 java.lang.Enum 클래스에 선언된 메소드인데, 열거 객체에서 사용할 수 있는 이유는 모든 열거 타입은 컴파일시에 Enum 클래스를 상속하게 되어 있기 때문이다. 상속에 대해서는 나중에 나오니, 잘 몰라도 상관없으니 그냥 넘어가도록 하자.
리턴 타입 | 메소드(매개 변수) | 설명 |
String | name() | 열거 객체의 문자열을 리턴 |
int | ordinal() | 열거 객체의 순번(0부터 시작)을 리턴 |
int | compareTo() | 열거 객체를 비교해서 순번 차이를 리턴 |
열거 타입 | valueOf(String name) | 주어진 문자열의 열거 객체를 리턴 |
열거 배열 | values() | 모든 열거 객체들을 배열로 리턴 |
열거 객체의 메소드 - name() 메소드
name() 메소드는 열거 객체가 가지고 있는 문자열을 리턴한다. 이때 리턴되는 문자열은 열거 타입을 정의할 때 사용한 상수 이름과 동일하다. 아래 코드는 today가 참조하는 열거 객체에서 name() 메소드를호출하여 문자열을 얻어낸다. name() 메소드는 열거 객체 내부의 문자열인 "SUNDAY"를 리턴하고 name 변수에 저장한다.
Week today = Week.SUNDAY;
String name = today.name();
열거 객체의 메소드 - ordinal() 메소드
ordinal() 메소드는 전체 열거 객체 중 몇 번째 열거 객체인지 알려준다. 열거 객체의 순번은 열거 타입을 정의할 때 주어진 순번을 말하는데, 0번부터 시작한다. 예를 들어 열거 타입 Week의 열거 객체 순번은 다음과 같이 결정된다.
public enum Week {
MONDAY, // 0번
TUESDAY, // 1번
WEDNESDAY, // 2번
THURSDAY, // 3번
FRIDAY, // 4번
SATURDAY, // 5번
SUNDAY // 6번
}
아래 코드는 today가 참조하는 열거 객체가 전체 열거 객체에서 몇 번째 순번인지 알아내는 코드이다. ordinal() 메소드는 6을 리턴해서 ordinal 변수에 저장한다.
Week today = Week.SUDNAY;
int ordinal = today.ordinal();
열거 객체의 메소드 - compareTo() 메소드
compareTo() 메소드는 매개값으로 주어진 열거 객체를 기준으로 전후로 몇 번째 위치하는지 비교한다. 만약 열거 객체가 매개 값의 열거 객체보다 순번이 빠르다면 음수가, 순번이 늦다면 양수가 리턴된다. 아래 코드는 day1과 day2의 상대적 위치를 비교하는 코드이다.
Week day1 = Week.MONDAY;
Week day2 = Week.WEDNESDAY;
int result1 = day1.compareTo(day2); // -2
int result2 = day2.compareTo(day1); // 2
day1.compareTo(day2)는 day2를 기준으로 day1의 상대적 위치를 리턴한다. day1(순번 : 0)이 day2(순번 : 2) 보다 앞에 위치하고 순번 차이가 2이므로 result1은 음수인 -2가 저장된다. 그러나 day2.compareTo(day1)은 day1을 기준으로 day2의 상대적 위치를 리턴한다. 따라서 day2가 day1보다 뒤에 위치하고 순번 차이가 2이므로 result는 양수 2가 저장된다.
열거 객체의 메소드 - valueOf() 메소드
valueOf() 메소드는 매개값으로 주어지는 문자열과 동일한 문자열을 가지는 열거 객체를 리턴한다. 이 메소드는 외부로부터 문자열을 입력받아 열거 객체로 변환할 때 유용하게 사용할 수 있다.
다음 코드에서 weekDay 변수는 Week.SATURDAY 열거 객체를 참조하게 된다.
Week weekDay = Week.valueOf("SATURDAY");
열거 객체의 메소드 - values() 메소드
values() 메소드는 열거 타입의 모든 열거 객체들을 배열로 만들어 리턴한다. 다음은 Week 열거 타입의 모든 열거 객체를 배열로 만들어 향상된 for문을 이용해서 반복하는 코드이다.
Week[] days = Week.values();
for(Week day : days) {
System.out.println(day);
}
Week 배열의 인덱스는 열거 객체의 순번과 같고, 각 인덱스 값은 해당 순번의 열거 객체 번지이다.