在Scala中创建Java Enum

col*_*evk 16 java enums scala

我的工作场所一直在尝试从Java迁移到Scala执行某些任务,并且它适用于我们正在做的事情.但是,一些预先存在的测井方法需要一个java.lang.Enum.日志记录方法在(Java)基类中定义,子类可以定义自己的枚举,记录器将跟踪多个线程/机器中的所有实例.

它在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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,当任务完成后,我们可以看到

BAD_THING: 230
GOOD_THING: 10345
Run Code Online (Sandbox Code Playgroud)

有没有办法在Scala中复制它,通过创建Java Enum或转换Enumeration为Enum?我试过Enum直接扩展,但它似乎是密封的,因为我在控制台中得到错误:

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
Run Code Online (Sandbox Code Playgroud)

Gle*_*est 23

Java枚举

对于枚举类Counter来说,名称会比一个更好的名称Counters- 每个枚举值代表一个单数计数器.

当javac编译一个enum类时,它:

  1. 编译为Counter包含所有构造函数,方法,枚举的其他成员(如果有)的普通java类(Eg )
  2. 每个enum值(GOOD_THING,BAD_THING)都是public static(1)的字段 - 类等于(1)(Counter)中的类:

    // Java Code:
    class Counter {
        public static Counter GOOD_THING;
        public static Counter BAD_THING;
    
        // constructors, methods, fields as defined in the enum ...
    
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 类中的初始化逻辑自动将每个enum值构造为单个对象

Scala选项

A.从Scala引用Java Enum

导入计数器,就像在java中一样引用GOOD_THING和BAD_THING,并且(如果你愿意)另外调用Enum类方法:

// 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?")
}
Run Code Online (Sandbox Code Playgroud)

优点:可以执行java枚举所做的所有操作,并且支持模式匹配.Disadvanges:因为基本特征不是sealed,所以任何进行模式匹配的代码都不会被打字检查以确保涵盖详尽的案例.

B.使用Scala枚举

将java转换enum为等效的scala Enumeration:

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

用它:

// 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?")
}
Run Code Online (Sandbox Code Playgroud)

优点:Scala的Enumeration方法与Java一样丰富Enum,并且支持模式匹配.Disadvanges:不能做java所做的一切enum- java enum被定义为一个具有任意构造函数,方法和其他成员允许的类(即enum basetype上的完整OO建模).因为基本特征不是sealed,所以不对任何进行模式匹配的代码进行类型检查以确保涵盖详尽的案例.

C.使用Scala案例类:

可以将enums直接转换为Case对象(即单例对象而不是Case Class,它不是单例):

sealed trait Counter
object Counter {
  case object GoodThing extends Counter;
  case object BadThing extends Counter; 
}
Run Code Online (Sandbox Code Playgroud)

用它:

// 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?")
}
Run Code Online (Sandbox Code Playgroud)
  • 优于枚举:每个值可以具有不同的祖先或不同的混合特征(只要每个值符合类型计数器).可以为特征计数器和每个值进行arbirtrarily复杂的OO建模.然后可以使用针对每个不同值的所有不同的案例对象参数来进行任意复杂的模式匹配.通过具有基本特征sealed,对进行模式匹配的任何代码进行类型检查以确保涵盖详尽的案例.(对您的要求无用).
  • 枚举的缺点:不要"免费"获取枚举方法(即值,带名称,应用程序).可以通过向基类计数器添加自定义实现来"修复"(稍微有点贬低,因为它是手动编码......).


Dan*_*ral 16

如果需要java枚举,那么需要用Java编写它.有些事情你可以做的Scala来代替使用情况的Enum,但没有什么Scala中,它会复制Java的机制Enum.


Jam*_*pic 5

虽然它可能不是一个好主意(见实际的好点子其他职位),它是可以扩展java.lang.Enum的斯卡拉。如果将类及其伴随对象放在同一个编译单元中,则代码将行得通(在REPL中,除非使用:pastemode ,否则每个语句都在其自己的编译单元中执行)。

如果您使用:pastemode,并粘贴以下代码,Scala会很高兴地对其进行编译:

sealed class AnEnum protected(name: String, ordinal: Int) extends java.lang.Enum[AnEnum](name, ordinal)
object AnEnum {
  val ENUM1 = new AnEnum("ENUM1",0)
  case object ENUM2 extends AnEnum("ENUM2", 1) // both vals and objects are possible
}
Run Code Online (Sandbox Code Playgroud)

但是,Java互操作可能不会令人满意。Java编译器将静态values和valueOf方法添加到新enum类中,并确保名称和序数正确,而Scala则不然。

即使您自己采取这些步骤,Java也不会信任您的枚举,因为该类没有ENUM修饰符。这表示Class::isEnum您的类不是枚举,例如,它将影响静态Enum::valueOf方法。Java的switch语句也不能与它们一起使用(尽管如果枚举值是case对象,则Scala的模式匹配应该起作用)。