Scala

Java의 Enum과 Scala의 Enum

partner_jun 2017. 5. 16. 16:18

Enumeration, 줄여서 enum이라고 부르는 열거 자료형은 자바나 스칼라 모두에서 아주 유용하게 쓰인다. Effective Java에 따르면 enum은 JVM에서 지원하는 싱글톤으로써 syncronized된 'getInstance' 메소드들을 선언할 필요 없이 아주 간편하게 만들 수 있고(메소드나 필드에서는 syncronized 키워드가 필요할 수 있다), 프로그램 전역에서 호출이 가능한 장점이 있다. 



1. 자바에서의 enum

자바의 enum은 열거 자료형으로써의 역할을 충실하게 수행해준다. 

public enum Fruits {
Apple, Banana
}

public static void main(String[] args) {
// 기본적인 호출
Fruits fruits = Fruits.Apple;

System.out.println(fruits); // Apple
System.out.println(fruits.name()); // Apple
System.out.println(fruits.ordinal()); // 0

// 문자열을 이용한 열거 자료형 호출
Fruits fruits2 = Fruits.valueOf("Banana"); // Fruits.Banana와 같음.

System.out.println(fruits2); // Banana
System.out.println(fruits2.name()); // Banana
System.out.println(fruits2.ordinal()); // 1
}

열거된 순서를 반환하는 .ordinal() 메소드는 enum 선언 순서를 따르므로 주의해서 사용해야 한다.



그 뿐 아니라, 메소드나 생성자를 선언해 각각을 객체로써 기능할 수 있게 할 수도 있다.

public enum Fruits {
Apple("RED"), Banana("YELLOW");


private String color;

Fruits(String color) { // enum의 생성자는 항상 private이다.
// enum 안에서만 생성 할 수 있기 때문이다.
this.color = color;
}

public String getColor() {
return this.color;
}
}

public static void main(String[] args) {
Fruits fruits = Fruits.Banana;
String color = fruits.getColor();
System.out.println(color); // YELLOW
}


앞서 말한대로 enum을 싱글톤 객체로 사용하려면 열거 자료형을 하나만 만들면 된다.

public enum FruitBox implements Serializable {
// enum은 인터페이스를 구현 할 수는 있지만 클래스를 상속 받을 수 없다.
// 또, enum은 final로 선언되기 때문에 enum을 상속받을 수도 없다.

getInstance; // 'getInstance'만 선언.

private List<Fruits> fruitsList = new ArrayList<>();

public boolean addFruits(Fruits fruits) {
return fruitsList.add(fruits);
}

public List<Fruits> getFruits() {
return fruitsList;
}
}


public static void main(String[] args) {

FruitBox fruitBox = FruitBox.getInstance;
fruitBox.addFruits(Fruits.Apple);
fruitBox.addFruits(Fruits.Banana);

System.out.println(fruitBox.getFruits().toString()); // [Apple, Banana]
}




2. 스칼라에서의 Enumeration

스칼라의 Enumeration은 자바처럼 키워드로 지원되지 않고 object에 Enumeration 클래스를 상속받아서 구현한다.

object Fruits extends Enumeration {
val Apple = Value
val Banana = Value(5)
val Orange = Value("Or")
}

val fruits = Fruits.withName("Apple")
println(fruits) // Apple
println(fruits.id) // a

val fruits2 = Fruits.Banana
println(fruits2) // Banana
println(fruits2.id) // 5

val fruits3 = Fruits.Orange
println(fruits3) // Or
println(fruits3.id) // 6

자바에서 enum 필드 이름의 파라미터는 enum의 생성자로 사용되었던 것과 달리 스칼라에서는 Value의 파라미터가 enum의 값이나 id를 바꾼다. Value의 파라미터에 따라 enum의 값이 전혀 달라질 수 있다는 점을 기억해야 한다.


여기서 추측할 수 있는 것처럼, 스칼라의 Enumeration은 자바와 달리 새로 정의된 클래스가 아니라 Value의 객체다.

object Fruits extends Enumeration {
// Value는 Enumeration 추상 클래스에 구현되어 있다.
val Apple: Fruits.Value = Value

}

val fruits: Fruits.Value = Fruits.withName("Apple")
println(fruits.getClass) // class scala.Enumeration$Val


때문에 자바처럼 각각의 객체가 사용 할 수 있는 메소드를 만들 수는 없다. 

하지만 implicit class를 선언하면 비슷한 형태로 메소드 사용이 가능해진다.

object Fruits extends Enumeration {
val Apple, Banana = Value


/**
* @param enums Friuts의 Value
*/
implicit class EnumsMethod(enums: Value) {

/**
* @return Fruit의 종류에 따른 색상 문자열
*/
def getColor: String = enums match {
case Apple => "RED"
case Banana => "YELLOW"
case _ => "NONE"
}
}
}

val fruits = Fruits.Banana
println(fruits.getColor) // YELLOW

Fruits의 Value를 생성자로 받는 암시적 클래스를 만들고 그 클래스 안에 메소드를 구현함으로써 Enumeration의 Value에서 메소드를 호출 할 수 있게 만드는 것이다. 



하지만 이전 implicit에 대해 작성한 포스트에서 알아봤던 것처럼, 암시적 클래스의 메소드는 호출될 때마다 클래스를 만든다는 단점이 있다.

object Fruits extends Enumeration {
val Apple, Banana = Value


/**
* @param enums Friuts의 Value
*/
implicit class EnumsMethod(enums: Value) {
println("new class. " + this)

/**
* @return Fruit의 종류에 따른 색상 문자열
*/
def getColor: String = enums match {
case Apple => "RED"
case Banana => "YELLOW"
case _ => "NONE"
}
}
}

val fruits = Fruits.Banana
val fruits2 = Fruits.Apple

println(fruits.getColor)
// new class. blog.EnumTest$Fruits$EnumsMethod@35bbe5e8
// YELLOW

println(fruits.getColor)
// new class. blog.EnumTest$Fruits$EnumsMethod@6bdf28bb
// YELLOW

println(fruits2.getColor)
// new class. blog.EnumTest$Fruits$EnumsMethod@6b71769e
// RED



스칼라의 Enumeration은 자바와 굉장히 다르게 느껴지지만 Programing Scala에 따르면 바이트코드로 컴파일되면 자바의 enum과 같다고 한다.