생활코딩 - enum 강의를보고 공부한 내용입니다
상수란 변하지 않는 값을 말한다.
📂 다음 코드를 보면 사과, 복숭아, 바나나가 숫자에 의미가 담겨있고 주석으로 각 숫자가 어떤 과일을 의미하는지 전달하고 있다. 이렇게 코드를 작성했을 때, 주석이 없어지거나 주석이 다른 곳에 작성된다면 제 3자 입장에서도 수정해야하는 개발자입장에서도 의미를 파악할 수 없어서 곤란하다.
package org.opentutorials.javatutorials.constant2;
public class ConstantDemo {
public static void main(String[] args) {
/*
* 1. 사과
* 2. 복숭아
* 3. 바나나
*/
int type = 1;
switch(type){
case 1:
System.out.println(57);
break;
case 2:
System.out.println(34);
break;
case 3:
System.out.println(93);
break;
}
}
}
🔍 그래서 다음과 같이 수정한다. 숫자가 아니라 이름으로 한다면 저 좋을 것이다. 그래서 변수를 지정하고 그 변수를 final로 처리하면 한번 설정된 값은 바뀌지 않는다. 또한 바뀌지 않는 값이라면 인스턴스 변수가 아니라 클래스 변수로 설정해준다.
-> 주석이 없더라도 변수만 봐도 어떤 것을 의미하는지 알 수 있게 된다
package org.opentutorials.javatutorials.constant2;
public class ConstantDemo {
private final static int APPLE = 1; <- 추가
private final static int PEACH = 2; <- 추가
private final static int BANANA = 3; <- 추가
public static void main(String[] args) {
int type = APPLE;
switch(type){
case APPLE:
System.out.println(57+" kcal");
break;
case PEACH:
System.out.println(34+" kcal");
break;
case BANANA:
System.out.println(93+" kcal");
break;
}
}
}
📂 그러다가 이 프로그램에서 기업에 대한 상수도 필요해져서 다음과 코드를 작성했다
package org.opentutorials.javatutorials.constant2;
public class ConstantDemo {
// fruit
private final static int APPLE = 1;
private final static int PEACH = 2;
private final static int BANANA = 3;
// company
private final static int GOOGLE = 1;
//private final static int APPLE = 2;
private final static int ORACLE = 3;
public static void main(String[] args) {
int type = APPLE;
switch(type){
case APPLE:
System.out.println(57+" kcal");
break;
case PEACH:
System.out.println(34+" kcal");
break;
case BANANA:
System.out.println(93+" kcal");
break;
}
}
}
🔍 위와 같이 작성했을 때 과일과 기업의 이름이 겹치게되어 프로그램이 오작동한다. 그래서 변수명에 접두사를 붙이는 방식으로 문제를 해결해보았다
package org.opentutorials.javatutorials.constant2;
public class ConstantDemo {
// fruit
private final static int FRUIT_APPLE = 1; <- 수정
private final static int FRUIT_PEACH = 2; <- 수정
private final static int FRUIT_BANANA = 3; <- 수정
// company
private final static int COMPANY_GOOGLE = 1; <- 수정
private final static int COMPANY_APPLE = 2; <- 수정
private final static int COMPANY_ORACLE = 3; <- 수정
public static void main(String[] args) {
int type = FRUIT_APPLE;
switch(type){
case FRUIT_APPLE:
System.out.println(57+" kcal");
break;
case FRUIT_PEACH:
System.out.println(34+" kcal");
break;
case FRUIT_BANANA:
System.out.println(93+" kcal");
break;
}
}
}
🔍 그런데 변수명이 너무 지저분하기 때문에 인터페이스를 사용하여 깔끔하게 정리할 수 있다.
package org.opentutorials.javatutorials.constant2;
interface FRUIT{ <- 추가
int APPLE=1, PEACH=2, BANANA=3;
}
interface COMPANY{ <- 추가
int GOOGLE=1, APPLE=2, ORACLE=3;
}
public class ConstantDemo {
public static void main(String[] args) {
int type = FRUIT.APPLE; <- 수정
switch(type){
case FRUIT.APPLE: <- 수정
System.out.println(57+" kcal");
break;
case FRUIT.PEACH: <- 수정
System.out.println(34+" kcal");
break;
case FRUIT.BANANA: <- 수정
System.out.println(93+" kcal");
break;
}
}
}
🔍 하지만 이렇게 작성했을 때 다음과 같은 문제가 생길 수 있다. 누군가 과일관련 변수와 기업관련 변수를 비교하는 코드를 실행한다고 한다. 이것은 과일과 기업을 비교하는 잘못된 코드임에도 불구하고 컴파일러는 이 오류를 찾지 못하고 동작시킨다. 그 이유는 둘의 변수명이 int로 같기 때문에 비교를 할 수 있는 것이다. 우리는 이것을 방지하기 위해서 클래스를 만들어준다.
-> 이렇게 함으로써 과일과 기업은 서로 다른 그룹의 상수이기 때문에 Fruit.APPLE == Company.APPLE은 동작하지 않게되고 컴파일 시에 오류가 검출될 수 있게 되었다
package org.opentutorials.javatutorials.constant2;
class Fruit{ <- 수정
public static final Fruit APPLE = new Fruit();
public static final Fruit PEACH = new Fruit();
public static final Fruit BANANA = new Fruit();
}
class Company{ <- 수정
public static final Company GOOGLE = new Company();
public static final Company APPLE = new Company();
public static final Company ORACLE = new COMPANY(Company);
}
public class ConstantDemo {
public static void main(String[] args) {
if(Fruit.APPLE == Company.APPLE){
System.out.println("과일 애플과 회사 애플이 같다.");
}
}
}
하지만 위의 코드는 다음과 같은 단점을 가지고 있다.
1. switch문에서 사용할 수 없다(몇가지 제한된 데이터 타입만을 사용할 수 있다)
2. 하나의 선언이 복잡하다
enum은 열거형이라고 부른다. 열거형은 서로 연관된 상수들의 집합이라고 할 수 있다. 위의 예제에서 과일과 기업이 열거인 셈이다
java에서는 이것을 문법적으로 지원하는게 그것이 바로 enum이다
package org.opentutorials.javatutorials.constant2;
enum Fruit{ <- 수정
APPLE, PEACH, BANANA;
}
enum Company{ <- 수정
GOOGLE, APPLE, ORACLE;
}
public class ConstantDemo {
public static void main(String[] args) {
/*
if(Fruit.APPLE == Company.APPLE){
System.out.println("과일 애플과 회사 애플이 같다.");
}
*/
Fruit type = Fruit.APPLE;
switch(type){
case APPLE:
System.out.println(57+" kcal");
break;
case PEACH:
System.out.println(34+" kcal");
break;
case BANANA:
System.out.println(93+" kcal");
break;
}
}
}
🔍 enum은 사실상 class이며 enum만의 문법적 형식을 가지고 있기 때문에 enum 키워드를 사용한다. 아래 두개의 코드는 똑같은 역할을 하고 있다. 하지만 둘을 비교하면 enum을 사용한 코드가 훨씬 깔끔하다는 것을 알 수 있다. 또 enum도 서로 다른 상수 그룹에 대한 비교를 컴파일 시점에서 차단할 수 있다.
📃 enum을 사용한 코드
enum Fruit{
APPLE, PEACH, BANANA;
}
📃 class를 사용한 코드
class Fruit{
public static final Fruit APPLE = new Fruit();
public static final Fruit PEACH = new Fruit();
public static final Fruit BANANA = new Fruit();
private Fruit(){}
}
🔍 enum을 사용하는 이유를 정리하면 다음과 같다
1. 코드가 단순해진다
2. 인스턴스 생성과 상속을 방지한다
3. 키워드 enum을 사용하기 때문에 구현의 의도가 열거임을 분명하게 나타낼 수 있다
📂 enum은 클래스기 때문에 enum을 어떻게 활용할 수 있을지 알아보자
🔍 Call Constructor가 세번 출력된다. 이것을 보아 필드의 숫자만큼 생성자가 호출되었다는 것을 알 수 있다. 즉 위에서 봤던 class를 사용한 코드와 똑같이 작동한다는 것도 확인할 수 있다
-> 또한 enum의 생성자가 접근 제어자 private만을 허용하기 때문에 직접 생성할 수는 없다
package org.opentutorials.javatutorials.constant2;
enum Fruit{
APPLE, PEACH, BANANA;
Fruit(){ <- 추가
System.out.println("Call Constructor "+this);
}
}
enum Company{
GOOGLE, APPLE, ORACLE;
}
public class ConstantDemo {
public static void main(String[] args) {
/*
if(Fruit.APPLE == Company.APPLE){
System.out.println("과일 애플과 회사 애플이 같다.");
}
*/
Fruit type = Fruit.APPLE;
switch(type){
case APPLE:
System.out.println(57+" kcal");
break;
case PEACH:
System.out.println(34+" kcal");
break;
case BANANA:
System.out.println(93+" kcal");
break;
}
}
}
🔍 그러면 enum은 생성자의 매개변수를 통해 인스턴스 변수값을 부여할 수 없을까? 다음 코드와 같이 작성하여 부여할 수 있다
enum Fruit{
APPLE("red"), PEACH("pink"), BANANA("yellow"); <- 수정
public String color; <- 추가
Fruit(String color){ <- 수정
System.out.println("Call Constructor "+this);
this.color = color;
}
}
public class ConstantDemo {
public static void main(String[] args) {
/*
if(Fruit.APPLE == Company.APPLE){
System.out.println("과일 애플과 회사 애플이 같다.");
}
*/
Fruit type = Fruit.APPLE;
switch(type){
case APPLE:
System.out.println(57+" kcal, "+Fruit.APPLE.color); <- 수정
break;
case PEACH:
System.out.println(34+" kcal"+Fruit.PEACH.color); <- 수정
break;
case BANANA:
System.out.println(93+" kcal"+Fruit.BANANA.color); <- 수정
break;
}
}
}
🔍 enum은 멤버 전체를 열거할 수 있는 기능도 제공한다
package org.opentutorials.javatutorials.constant2;
enum Fruit{
APPLE("red"), PEACH("pink"), BANANA("yellow");
private String color;
Fruit(String color){
System.out.println("Call Constructor "+this);
this.color = color;
}
String getColor(){ <- 추가
return this.color;
}
}
enum Company{
GOOGLE, APPLE, ORACLE;
}
public class ConstantDemo {
public static void main(String[] args) {
for(Fruit f : Fruit.values()){ <- 추가
System.out.println(f+", "+f.getColor());
}
}
}
열거형의 특성을 정리해보면 열거형은 연관된 값들을 저장한다. 또 그 값들이 변경되지 않도록 보장한다. 뿐만 아니라 열거형 자체가 클래스이기 때문에 열거형 내부에 생성자, 필드, 메소드를 가질 수 있어서 단순히 상수가 아니라 더 많은 역할을 할 수 있다.