Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a Java Enum in Scala

Tags:

java

enums

scala

My workplace has been experimenting in moving from Java to Scala for some tasks, and it works well for what we're doing. However, some preexisting logging methods expect a java.lang.Enum. The logging method is defined in the (Java) base class, and the subclasses can define their own enums, which the logger will track across all instances in multiple threads/machines.

It works like this in Java:

public class JavaSubClass extends JavaBaseClass {
    enum Counters {
        BAD_THING,
        GOOD_THING
    }

    public void someDistributedTask() {
        // some work here
        if(terribleThing) {
            loggingMethod(Counters.BAD_THING)
        } else {
            loggingMethod(Counters.GOOD_THING)
            // more work here
        }
    }
}

Then, when the task has finished, we can see that

BAD_THING: 230
GOOD_THING: 10345

Is there any way to replicate this in Scala, either by creating Java Enums or converting from Enumeration to Enum? I have tried extending Enum directly, but it seems to be sealed, as I get the error in the console:

error: constructor Enum in class Enum cannot be accessed in object $iw
Access to protected constructor Enum not permitted because
enclosing object $iw is not a subclass of 
class Enum in package lang where target is defined
like image 937
colevk Avatar asked Dec 05 '13 22:12

colevk


People also ask

How do you create an enum in Scala?

In Scala, there is no enum keyword unlike Java or C. Scala provides an Enumeration class which we can extend in order to create our enumerations. Every Enumeration constant represents an object of type Enumeration. Enumeration values are defined as val members of the evaluation.

What are enum types?

Enumerated (enum) types are data types that comprise a static, ordered set of values. They are equivalent to the enum types supported in a number of programming languages. An example of an enum type might be the days of the week, or a set of status values for a piece of data.

What does enum valueOf return?

valueOf. Returns the enum constant of the specified enum type with the specified name. The name must match exactly an identifier used to declare an enum constant in this type. (Extraneous whitespace characters are not permitted.)


1 Answers

Java Enums

For an enum class Counter would be a better name than Counters - each enum value represents a singular counter.

When javac compiles an enum class, it:

  1. compiles to a normal java class (E.g. Counter) containing all of the constructors, methods, other members of the enum (if any)
  2. each enum value (GOOD_THING, BAD_THING) is made a public static field of (1) - with class equal to the class in (1) (Counter):

    // Java Code:
    class Counter {
        public static Counter GOOD_THING;
        public static Counter BAD_THING;
    
        // constructors, methods, fields as defined in the enum ...
    
    }
    
  3. initialization logic in the class automatically constructs each enum value as a singleton object

Scala Options

A. Reference Java Enum From Scala

Import Counter, refer to GOOD_THING and BAD_THING just like in java, and (if you like) additionally call Enum class methods:

// Scala Code:
import JavaSubClass.Counter;

def someDistributedTask = {
    // some work here
    if (terribleThing) {
        loggingMethod(Counter.BAD_THING)
    } else {
        loggingMethod(Counter.GOOD_THING)
        // more work here
    }
}

// Other things you can do:
val GoodThing = Counter.valueOf("GOOD_THING")

Counter.values() foreach { // do something }

counter match {
  case Counter.GOOD_THING => "Hoorah"
  case Counter.BAD_THING => "Pfft"
  case _ => throw new RuntimeException("someone added a new value?")
}

Advantages: Can do everything that java enumerations do, plus supports pattern matching. Disadvanges: Because the base trait is not sealed, any code doing pattern matching is not typed-checked to ensure exhaustive cases are covered.

B. Use Scala Enumeration

Convert java enum to equivalent scala Enumeration:

// Scala Code:
object Counter extends Enumeration {
  type Counter = Value
  val GoodThing = Value("GoodThing") 
  val BadThing = Value("BadThing")
}

Use it:

// Scala Code:
import someScalaPackage.Counter;

def someDistributedTask = {
    // some work here
    if (terribleThing) {
        loggingMethod(Counter.BadThing)
    } else {
        loggingMethod(Counter.GoodThing)
        // more work here
    }
}

// Other things you can do:
val GoodThing = Counter.withName("GoodThing")
val label = Counter.BadThing.toString

Counter.values foreach { // do something }

myCounter match {
  case Counter.GOOD_THING => "Bully!"
  case Counter.BAD_THING => "Meh"
  case _ => throw new RuntimeException("someone added a new value?")
}

Advantages: Scala's Enumeration methods are as rich as Java Enum's, plus supports pattern matching. Disadvanges: Can't do everything that java enums do - java enum's are defined as a class with arbitrary constructors, methods and other members allowable (i.e. full OO modelling on the enum basetype). Because the base trait is not sealed, any code doing pattern matching is not typed-checked to ensure exhaustive cases are covered.

C. Use Scala Case Classes:

Can convert enums directly into Case Objects (i.e. singleton objects as opposed to Case Class, which is not singleton):

sealed trait Counter
object Counter {
  case object GoodThing extends Counter;
  case object BadThing extends Counter; 
}

Use it:

// Scala Code:
import someScalaPackage.Counter;

def someDistributedTask = {
    // some work here
    if (terribleThing) {
        loggingMethod(Counter.BadThing)
    } else {
        loggingMethod(Counter.GoodThing)
        // more work here
    }
}

// Other things you can do:
// NO!!   val GoodThing = Counter.withName("GoodThing")
val label = Counter.BadThing.toString

// NO!!   Counter.values foreach { // do something }

myCounter match {
  case Counter.GOOD_THING => "Bully!"
  case Counter.BAD_THING => "Meh"
  case _ => throw new RuntimeException("someone added a new value?")
}
  • Advantage over Enumeration: each value can have a different ancestor or different mixin traits (as long as each value conforms to type Counter). Can do arbirtrarily complex OO modelling for the trait Counter and for each Value. Can then do arbitrarily complex pattern matching using all the different case object parameters for each different value. By having base trait sealed, any code doing pattern matching is typed-checked to ensure exhaustive cases are covered. (Not useful for your requirements).
  • Disadvantage over Enumeration: don't get Enumeration methods 'for free' (i.e. values, withName, application). Can be 'fixed' by adding custom implementations to base class Counter (a bit of a contridiction, since it's manual coding...).
like image 174
Glen Best Avatar answered Oct 14 '22 10:10

Glen Best