<aside> 😅 Enum? 열거형은 서로 관련된 상수를 편리하게 선언하기 위한 것으로 상수를 여러 개 정의할 때 사용한다. 열거형은 상수 정의 이후 정의된 것 이외의 값은 허용하지 않는다. 오라클 튜토리얼에는 열거형을 이렇게 설명하고 있다. 열거형 은 변수가 미리 정의된 상수 집합이 될 수 있도록 하는 특수 데이터 유형입니다 . 변수는 변수에 대해 미리 정의된 값 중 하나와 같아야 합니다. 즉, 열거형은 상수 집합을 만들때 사용한다는 것이다. 예를 들자면 뭐가 있을까? 바뀌지 않지만 집합의 형태를 가지고 있는 우리 일상 속의 것들이? 요일, 달력의 날짜, 국가명, RGB값? 이런것도 될 수 있지 않을까?
</aside>
public enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY
}
...
public class EnumTest {
Day day;
public EnumTest(Day day) {
this.day = day;
}
public void tellItLikeItIs() {
switch (day) {
case MONDAY:
System.out.println("Mondays are bad.");
break;
case FRIDAY:
System.out.println("Fridays are better.");
break;
case SATURDAY: case SUNDAY:
System.out.println("Weekends are best.");
break;
default:
System.out.println("Midweek days are so-so.");
break;
}
}
public static void main(String[] args) {
EnumTest firstDay = new EnumTest(Day.MONDAY);
firstDay.tellItLikeItIs();
EnumTest thirdDay = new EnumTest(Day.WEDNESDAY);
thirdDay.tellItLikeItIs();
EnumTest fifthDay = new EnumTest(Day.FRIDAY);
fifthDay.tellItLikeItIs();
EnumTest sixthDay = new EnumTest(Day.SATURDAY);
sixthDay.tellItLikeItIs();
EnumTest seventhDay = new EnumTest(Day.SUNDAY);
seventhDay.tellItLikeItIs();
}
}
CONSOLE RESULT
Mondays are bad.
Midweek days are so-so.
Fridays are better.
Weekends are best.
Weekends are best
<aside>
😅 열거형 내부에는 메서드, 기타 필드가 포함될 수 있다. 단, 컴파일러는 열거형을 만들때 values(), valueOf() 추가해서 만들어준다. 예를 들자면 다음과 같다.
***- values() : 선언된 순서대로 열거형의 모든 값을 포함하는 배열을 반환하는 메서드 컴파일하면서 생긴다.
T valueOf(Class<T> enumType, String name) : 지정된 열거형에서 name과 일치하는 열거형 상수를 반환한다. 컴파일하면서 생긴다.Class<E> getDeclaringClass() : 열거형의 객체를 반환한다.String name() : 열거형 상수의 이름을 문자열로 반환한다.int ordinal() : 열거형 상수가 정의된 순서를 반환한다.(0부터 시작한다)***</aside>
<aside>
📌 ordinal()?
문제가 있다. 이걸 기반해서 코드를 작성하면 위험하다. 왜냐하면 0~… int를 반환하는데 순서가 바뀌면 위험할 수 있다. 대부분의 프로그래머들은 이걸 쓸 일 없다. 내부용이다.
</aside>
public enum Planet {
MERCURY (3.303e+23, 2.4397e6),
VENUS (4.869e+24, 6.0518e6),
EARTH (5.976e+24, 6.37814e6),
MARS (6.421e+23, 3.3972e6),
JUPITER (1.9e+27, 7.1492e7),
SATURN (5.688e+26, 6.0268e7),
URANUS (8.686e+25, 2.5559e7),
NEPTUNE (1.024e+26, 2.4746e7);
private final double mass; // in kilograms
private final double radius; // in meters
Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
}
private double mass() { return mass; }
private double radius() { return radius; }
// universal gravitational constant (m3 kg-1 s-2)
public static final double G = 6.67300E-11;
double surfaceGravity() {
return G * mass / (radius * radius);
}
double surfaceWeight(double otherMass) {
return otherMass * surfaceGravity();
}
public static void main(String[] args) {
if (args.length != 1) {
System.err.println("Usage: java Planet <earth_weight>");
System.exit(-1);
}
double earthWeight = Double.parseDouble(args[0]);
double mass = earthWeight/EARTH.surfaceGravity();
for (Planet p : Planet.values())
System.out.printf("Your weight on %s is %f%n",
p, p.surfaceWeight(mass));
}
}
<aside>
😅 이렇게 보면 Enum을 왜 만들었나 싶다. 당근 class에 static final로 상수를 정의하면 될 것 같은데 왜 만든 것일까?필자가 찾아보고 개인적인 견해에 의해서 적어본다면 데이터의 추상적인 부분을 가시적으로 만들어주기 위함이라고 생각한다. 예를 들자면 맥북을 판매하는 서비스를 만든다고 가정해보자. 그럼 다음과 같은 코드를 만들 수 있다.
</aside>
public class Test{
static class MacBook{
public static final int MAC_13= 1_000_000;
public static final int MAC_16= 2_000_000;
}
public static void main(String[] args) {
int price =getPrice(MacBook.MAC_13); // 100만원 입니다. 출력
}
public static int getPrice(int price){
int type = price;
switch (type) {
case MacBook.MAC_13:
System.out.println("100만원 입니다.");
return MacBook.MAC_13;
case MacBook.MAC_16:
System.out.println("200만원 입니다.");
return MacBook.MAC_16;
default:
throw new NullPointerException("가격정보가 없습니다");
}
}
}
<aside> 😅 하지만 이것은 맥북이 인텔 맥북만 존재할때 이야기였다. 그런데 맥북에 m1 맥북이 추가되어 우리는 상수를 고쳐야할 일이 생겨버렸다. 하지만 인텔맥과 m1 맥이 동일한 가격 동일한 성능으로 출시되었기 때문에 이렇게 이름을 바꿔줄 수 밖에 없었다. 그런데 이렇게 하면 동일한 맥북인데 이름을 다르게 할 수 밖에 없었다. 또한 이렇게 하면 개발자의 실수를 부추기는 코드가 되어버린다. 그 예시는 아래 첫번째 코드와 같다. 다음 문제는 아주 심각한 문제를 일으킨다. 다른 메서드에 개념적으로 다른 어떠한 값을 집어넣어도 type이 동일하기 때문에 컴퓨터는 알아차리지 못한다(아까 언급한 추상적인 부분이다). 이 문제를 해결하기 위해서 아래의 두번째 코드와 같이 인터페이스를 사용하면 되지만 그건 안티패턴이다. 그럼 어떻게 해야할까?
</aside>
static class MacBook{
public static final int MAC_13 = 1_000_000;
public static final int MAC_16 = 2_000_000;
public static final int M1_MAC_13 = 1_000_000;
public static final int M1_MAC_16 = 2_000_000;
}
...
public class Test{
static class MacBook{
public static final int MAC_13 = 1_000_000;
public static final int MAC_16 = 2_000_000;
public static final int M1_MAC_13 = 1_000_000;
public static final int M1_MAC_16 = 2_000_000;
}
public static void main(String[] args) {
int price = getIntelMacPrice(MacBook.M1_MAC_13);
**// 인텔맥의 가격을 가져오는 메서드에 M1맥을 집어 넣어도 실행이 되어버린다.
// 내가 원하는 것은 인텔맥13형인데?.**
}
public static int getIntelMacPrice(int price){
int type = price;
switch (type) {
case MacBook.MAC_13:
System.out.println("100만원 입니다.");
return MacBook.MAC_13;
case MacBook.MAC_16:
System.out.println("200만원 입니다.");
return MacBook.MAC_16;
default:
throw new NullPointerException("가격정보가 없습니다");
}
}
}
interface IntelMac{
public static final int MAC_13 = 1_000_000;
public static final int MAC_16 = 2_000_000;
}
interface M1Mac{
public static final int MAC_13 = 1_000_000;
public static final int MAC_16 = 2_000_000;
}
<aside>
😅 이런 이유에서 Enum이 탄생하게 되었다. 위의 코드를 Enum Type으로 수정하면 다음과 같이 코드를 만들 수 있다. 조금 다른점은 이제는 Enum M1Mac에 정의된 MAC_13형을 넣으면 컴파일러가 잡아준다.

</aside>
public class Test{
enum IntelMac{
MAC_13(1_000_000),
MAC_16(2_000_000);
private int price;
IntelMac(int price) {
this.price = price;
}
public int getPrice(){
return price;
}
}
enum M1Mac{
MAC_13(1_000_000),
MAC_16(2_000_000);
private int price;
M1Mac(int price) {
this.price = price;
}
public int getPrice(){
return price;
}
}
public static void main(String[] args) {
int price = getIntelMacPrice(IntelMac.MAC_13);
}
public static int getIntelMacPrice(IntelMac mac){
switch (mac) {
case MAC_13:
System.out.println("100만원 입니다.");
return IntelMac.MAC_13.getPrice();
case MAC_16:
System.out.println("200만원 입니다.");
return IntelMac.MAC_16.getPrice();
default:
throw new NullPointerException("가격정보가 없습니다");
}
}
}
<aside> 😅 이렇듯 Enum은 서로 다른 개념의 충돌할때 개발자가 그걸 가시적으로 볼 수 있는 기회를 제공해준다.
</aside>
<aside>
😅 Enum의 특징은 엄연한 Type이라는 점이다(Type이 존재한다는건 특성 - 객체라는 의미와도 같다고 생각한다). 즉, Enum도 클래스다. 이 외에도 몇가지 특징을 나열하면 다음과 같다.
</aside>
Eunm의 생성자의 접근 제어자는 private이다.