Dev Kumar
Dev Kumar

Reputation: 93

How to create similar enum class in scala

This is how my enum class looks like in java. How do I create same in scala?

public interface EmployeeDataField {
int getPosition();
boolean isMandatory();
String name();
}

public enum EmployeeJobDataField implements EmployeeDataField {
EMP_ID(1, true), LOGINID(2), FIRST_NAME(3),
MIDDLE_NAME(4), LAST_NAME(5),
FULL_NAME(6), JOB_LEVEL(7)

EmployeeJobDataField(final int position) {
    this(position, false);
}
}

Upvotes: 0

Views: 166

Answers (1)

Mateusz Kubuszok
Mateusz Kubuszok

Reputation: 27535

Recommended way is to used a sealed trait and case objects:

sealed trait MyEnum
object MyEnum {
  case object One extends MyEnum
  case object Two extends MyEnum
}

If you need to have a list of all enums, "String" to value, etc, add to it enumeratum library

import enumeratum._

sealed trait MyEnum extends EnumEntry
object MyEnum extends Enum[MyEnum] {
  case object One extends MyEnum
  case object Two extends MyEnum

  val values = findValues
}

If you want to initialize each value with some parameter you can use sealed abstract class

sealed abstract class MyEnum(val someValue: Int)
object MyEnum {
  case object One extends MyEnum(1)
  case object Two extends MyEnum(2)
}

sealed abstract class can also be used with enumeratum.

If you want you can add methods to it.

import enumeratum._

sealed abstract class MyEnum(val someProperty: Int) extends EnumEntry {

  def someMethod: Int = someProperty * 2

  def someAbstractMethod: String
}
object MyEnum extends Enum[MyEnum] {
  case object One extends MyEnum {
    def someAbstractMethod: String = "a"
  }
  case object Two extends MyEnum {
    def someAbstractMethod: String = "b"
  }

  val values = findValues
}

Theoretically, Scala has a build-in Enumeration but it has many issues and is discouraged nowadays:

  • values of Enumeration are just values so we cannot check if match is exhaustive

    object MyEnum extends Enumeratum {
      val One, Two = Value
    }
    
    (myEnum: MyEnum) match {
      case One => ...
      // no error or warning on non-exhaustive match
    }
    
  • you cannot make Enumeration values implement interfaces, add methods or properties - they are just sets of objects with overridden toString that can be compared for reference equality. The object itself has some useful methods, but so does enumeratum
  • because of how it is implemented, type-class derivation for Enumeration is harder than for sealed hierarchies - for that reason many libraries don't support it at all

    @ import $ivy.`com.chuusai::shapeless:2.3.3`, shapeless._ 
    import $ivy.$                             , shapeless._
    
    // MyEnum doesn't exist as a separate type, which might be confusing
    // (MyEnum.type is the actual type of MyEnum object)
    @ Generic[MyEnum] 
    cmd7.sc:1: not found: type MyEnum
    val res7 = Generic[MyEnum]
                   ^
    Compilation Failed
    
    // and MyEnum.Value doesn't tell compiler anything useful
    @ Generic[MyEnum.Value] 
    cmd7.sc:1: could not find implicit value for parameter gen: shapeless.Generic[ammonite.$sess.cmd0.MyEnum.Value]
    val res7 = Generic[MyEnum.Value]
                  ^
    Compilation Failed
    

    If even libraries for type class derivation don't support it, you cannot rely on libraries using them to have that support. It can be done (I did it in enumz), but as far as I can say nobody does.

As a result many people independently reached conclusion that Enumeration doesn't work for them, while sealed traits do. If they need extra methods (all values, name-to-value and value-to-name conversion, etc) they add enumeratum and call it a day.

In Scala 3 the boilerplate will be reduced with enums:

enum MyEnum {
  case One, Two
}

which will bring a lot to the table (e.g. compatibility with Java enums if you need it).

Upvotes: 4

Related Questions