在Scala中创建Java Enum

我的工作场所一直在尝试从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 } } } 

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

 BAD_THING: 230 GOOD_THING: 10345 

有没有办法在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 

如果需要java枚举,那么需要用Java编写它。 你可以在Scala中做一些事情来替换Enum用例 ,但Scala中没有任何东西可以复制Enum的Java机制。

Java枚举

对于枚举类, Counter将是一个比Counters更好的名称 – 每个枚举值代表一个单数计数器。

当javac编译enum类时,它:

  1. 编译为包含所有构造函数,方法,枚举的其他成员(如果有)的普通java类(例如Counter
  2. 每个enum值( GOOD_THINGBAD_THING )成为(1)的public static字段 – 类等于(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. 类中的初始化逻辑自动将每个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?") } 

优点:可以执行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") } 

用它:

 // 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?") } 

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

C.使用Scala案例类:

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

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

用它:

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

虽然它可能不是一个好主意(请参阅其他post了解实际的好主意),但可以在Scala中扩展java.lang.Enum 。 如果您将类及其伴随对象放在同一个编译单元中(在REPL中,每个语句都在自己的编译单元中执行,除非您使用:paste模式),您的代码就可以工作了。

如果您使用:paste模式,并粘贴以下代码,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 } 

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

即使您自己采取这些步骤,Java也不会信任您的枚举,因为该类没有ENUM修饰符。 这意味着Class::isEnum会说你的类不是枚举,这会影响静态Enum::valueOf方法,例如。 Java的switch语句也不适用于它们(尽管Scala的模式匹配应该有效,如果枚举值是case对象)。