상수는 변하지 않는 값이다. 아래에서 좌향이 변수이고 우항이 상수이다.
int x = 1;
아래와 같은 구문은 있을 수 없다. 1은 2가 될 수 없다.
1 = 2;
상수의 이런 특성을 이용해서 아래와 같은 로직을 작성할 수 있다.
public class ConstantTest {
public static void main(String[] args) {
/*
* 1. 사과
* 2. 복숭아
* 3. 사과
*/
int type = 1;
switch(type) {
case 1:
System.out.println("apple");
break;
case 2:
System.out.println("peach");
break;
case 3:
System.out.println("banana");
break;
}
}
}
위와 같은 로직에서 숫자 1에 해당하는 과일은 언제나 사과이여야 한다. 그러므로 변하지 않는 값인 상수값에 따라서 그 값에 해당하는 과일의 의미를 고정하고 있다. 그런데 주석으로 상수의 의미를 전달하고 있지만 주석이 없어지거나, 주석이 상수를 사용하는 코드와 멀어진다면 각 숫자에 해당하는 과일이 무엇을 의미하는지 알아보기 어려운 문제가 있다.
이럴 때는 이름이 있다면 더 좋을 것이다. 즉, 변수도 상수가 될 수 있다. 변수를 지정하고 그 변수를 final로 처리하면 한번 설정된 변수의 값은 더이상 바뀌지 않는다.
또한 바귀지 않는 값이라면 인스턴스 변수가 아니라 클래스 변수(static)로 지정하는 것이 더 좋을 것이다.
class Constant1 {
public final static int APPLE = 1;
public final static int PEACH = 2;
public final static int BANANA = 3;
}
public class ConstantTest {
public static void main(String[] args) {
int type = Constant1.APPLE;
switch(type) {
case Constan1.APPLE:
System.out.println("apple");
break;
case Constan1.PEACH:
System.out.println("peach");
break;
case Constan1.BANANA:
System.out.println("banana");
break;
}
}
}
하지만 동일한 변수명의 상수가 추가될 경우는 어떻게 될까?
프로그램이 커지면서 기업에 대한 상수가 필요해져서 아래와 같이 코드를 변경했다.
class Constant1 {
// fruit
public final static int APPLE = 1;
public final static int PEACH = 2;
public final static int BANANA = 3;
// company
public final static int GOOGLE = 1;
// public final static int APPLE = 2; // 위에 이미 변수가 선언되어 있어 에러 발생
public final static int ORACLE = 3;
}
public class ConstantTest {
public static void main(String[] args) {
int type = Constant1.APPLE;
switch(type) {
case Constan1.APPLE:
System.out.println("apple fruit");
break;
case Constan1.PEACH:
System.out.println("peach fruit");
break;
case Constan1.BANANA:
System.out.println("banana fruit");
break;
}
}
}
이렇게 변경할 경우 이미 애플이라는 변수가 사용되고 있어 기업의 의미로 사용할 애플 변수를 과일에 해당하는 변수로 써야되는데 2가 아닌 1이라는 문제가 발생되어 접두사를 사용해보자.
class Constant1 {
// fruit
public final static int FRUIT_APPLE = 1;
public final static int FRUIT_PEACH = 2;
public final static int FRUIT_BANANA = 3;
// company
public final static int COMPANY_GOOGLE = 1;
public final static int COMPANY_APPLE = 2;
public final static int COMPANY_ORACLE = 3;
}
public class ConstantTest {
public static void main(String[] args) {
int type = Constant1.FRUIT_APPLE;
switch(type) {
case Constan1.FRUIT_APPLE:
System.out.println("apple fruit");
break;
case Constan1.FRUIT_PEACH:
System.out.println("peach fruit");
break;
case Constan1.FRUIT_BANANA:
System.out.println("banana fruit");
break;
}
}
}
이렇게 변경을 하면 이름이 중복될 확률이 낮출 수 있다. 이러한 기법을 네임스페이스라고 한다. 하지만 상수가 너무 지저분하다. 또한, 누군가 실수로 type 값을 Constant1.COMPANY_GOOGLE 로 변경하면 어떻게 될까?
...
public class ConstantTest {
public static void main(String[] args) {
int type = Constant1.COMPANY_GOOGLE;
switch(type) {
case Constan1.FRUIT_APPLE:
System.out.println("apple fruit");
break;
case Constan1.FRUIT_PEACH:
System.out.println("peach fruit");
break;
case Constan1.FRUIT_BANANA:
System.out.println("banana fruit");
break;
}
}
}
apple fruit
결과는 에러 없이 잘 작동하며, apple fruit 값이 나오게 된다. 위의 코드는 서로 다른 상수 그룹의 비교를 시도했고 양쪽 모두 값이 1이기 때문에 오류를 사전에 찾아주지 못하고 있다.
오류는 컴파일 시 나타나는 것이 가장 바랍직하다. 실행 중에 발생하는 오류는 찾아내기 더욱 어렵다.
이러한 문제들을 해결하는 방법을 없을까?
해결방법으로 enum 클래스를 사용하면 해결이 가능합니다. 우선 enum 이 무엇인지 확인해보자.
enum 이란
enum은 열거형(enumerated type)이라고 부른다. 열거형은 서로 연관된 상수들의 집합이라고 할 수 있다.
enum을 이용하면 아래의 코드로 바뀐다.
enum Fruit {
APPLE, PEACH, BANANA;
}
enum Company {
GOOGLE, APPLE, ORACLE;
}
public class ConstantTest {
public static void main(String[] args) {
Fruit type = Fruit.APPLE;
switch(type) {
case APPLE:
System.out.println("apple fruit");
break;
case PEACH:
System.out.println("peach fruit");
break;
case BANANA:
System.out.println("banana fruit");
break;
}
}
}
이렇게 코드도 단순해지고 가독성이 좋아지며, Fruit.APPLE를 Company.APPLE로 변경해도 컴파일 단계에서 오류를 잡아준다.
enum을 사용하는 이유
- 코드가 단순해지며, 가독성이 좋다.
- 인스턴스 생성과 상속을 방지하여, 상수값의 타입안정성이 보장된다.
- enum class를 사용해 새로운 상수들의 타입을 정의함으로 정의한 타입이외의 타입을 가진 데이터값을 컴파일 시 체크한다.
public class EnumExample {
static final String MALE = "MALE";
static final String FEMALE = "FEMALE";
public static void main(String[] args) {
String gender;
gender = EnumExample.MALE;
gender = EnumExample.FEMALE;
// 컴파일 에러가 발생하지 않음 -> 문제점 발생
gender = "boy";
Gender gender2;
gender2 = Gender.MALE;
gender2 = Gender.FEMALE;
// 컴파일 에러 발생 -> 의도하지 않은 상수 값을 체크할 수 있음
gender2 = "boy";
System.out.println(Fruit.APPLE.getColor()); // red 출력
}
}
enum Gender {
MALE, FEMALE;
}
enum Fruit {
APPLE("red"), BANANA("yellow");
private String color;
// 생성자의 매개변수를 통해 필드(APPLE, ...)의 인스턴스 변수 값을 부여할 수 있다.
Fruit(String color) {
this.color = color;
}
public String getColor() {
return this.color;
}
}