在 Scala 中创建 Java 枚举

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20412287/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 01:46:15  来源:igfitidea点击:

Creating a Java Enum in Scala

javascalaenums

提问by colevk

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.

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

It works like this in 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
        }
    }
}

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 Enumerationto Enum? I have tried extending Enumdirectly, but it seems to be sealed, as I get the error in the console:

有没有办法在 Scala 中复制它,通过创建 Java Enums 或从Enumerationto转换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

采纳答案by Daniel C. Sobral

If you need a java enumeration, then you need to write it in Java. There are things you can do in Scala to replace the use casesof Enum, but there's nothing in Scala that replicates the Java mechanics of Enum.

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

回答by Glen Best

Java Enums

Java 枚举

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

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

When javac compiles an enumclass, it:

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

  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 enumvalue (GOOD_THING, BAD_THING) is made a public staticfield 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 enumvalue as a singleton object

  1. 编译为Counter包含所有构造函数、方法、枚举的其他成员(如果有)的普通 java 类(例如)
  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 ...
    
    }
    
  3. 类中的初始化逻辑自动将每个enum值构造为单例对象

Scala Options

Scala 选项

A. Reference Java Enum From Scala

A. 从​​ Scala 引用 Java 枚举

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

导入计数器,就像在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?")
}

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.

优点:可以做java枚举可以做的一切,并且支持模式匹配。缺点:因为基本特征不是sealed,任何进行模式匹配的代码都不会进行类型检查以确保涵盖详尽的情况。

B. Use Scala Enumeration

B. 使用 Scala 枚举

Convert java enumto equivalent scala Enumeration:

将 java 转换enum为等效的 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 Enumerationmethods 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.

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

C. Use Scala Case Classes:

C. 使用 Scala 案例类:

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

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

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...).
  • 枚举的优势:每个值可以有不同的祖先或不同的混合特征(只要每个值符合类型 Counter)。可以为特征计数器和每个值进行任意复杂的 OO 建模。然后可以使用每个不同值的所有不同 case 对象参数进行任意复杂的模式匹配。通过拥有 base trait sealed,任何进行模式匹配的代码都会进行类型检查,以确保涵盖详尽的情况。(对您的要求没有用)。
  • 枚举的缺点:不要“免费”获得枚举方法(即值、withName、应用程序)。可以通过向基类 Counter 添加自定义实现来“修复”(有点矛盾,因为它是手动编码......)。

回答by James_pic

Whilst it's probably not a good idea (see other posts for actual good ideas), it ispossible to extend java.lang.Enumin Scala. Your code would have worked, if you'd put both the class and its companion object in the same compilation unit (in the REPL, each statement is executed in its own compilation unit, unless you use :pastemode).

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

If you use :pastemode, and paste in the following code, Scala will happily compile it:

如果使用: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
}

However, Java interop will probably not be satisfactory. The Java compiler adds static valuesand valueOfmethods to new enumclasses, and ensures that the names and ordinals are correct, which Scala will not.

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

Even if you take these steps yourselves, Java won't trust your enum because the class doesn't have the ENUMmodifier. This means that Class::isEnumwill say your class isn't an enum, which will affect the static Enum::valueOfmethod, for example. Java's switch statement won't work with them either (although Scala's pattern matching should work, if the enum values are case objects).

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

回答by VonC

As explained in this thread, Dottywill have enumfor Scala 3.0 (mid-2020, seven years later)

正如所解释的,在这个线程斑点狗enum斯卡拉3.0(中期到2020年,七年后)

Scala redesigned Enumsas well.
They can be parameterized and can contain custom members.

Scala 也进行了重新设计Enums
它们可以被参数化并且可以包含自定义成员。

object Day extends Enumeration {
  type Day = Value
  val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
}

//replaced with

enum Day {
  case Mon, Tue, Wed, Thu, Fri, Sat, Sun
}

From "Martin Odersky -- A tour of Scala 3" (June 2019):

摘自“ Martin Odersky -- Scala 3 之旅”(2019 年 6 月):

Enums can be parameterized.

枚举可以参数化。

enum Day(val mon: Int) {}

Enums:

  • can have parameters
  • can define fields and methods
  • can interop with Java

枚举:

  • 可以有参数
  • 可以定义字段和方法
  • 可以与Java互操作
enum Planet(mass: Double, radius: Double)
extends java.lang.Enum { 
  private final val G = 6.67300E-11 
  def surfaceGravity = G * mass / (radius * radius) 

  case MERCURY extends Planet(3.303e+23, 2.4397e6) 
  case VENUS extends Planet(4.869e+24, 6.0518e6) 
  case EARTH extends Planet(5.976e+24, 6.37814e6) 
  case MARS extends Planet(6.421e+23, 3.3972e6)
  ... 
} 

Enums can have type parameters, making them algebraic data types (ADTs)

枚举可以有类型参数,使它们成为代数数据类型 (ADT)

enum Option[+T] { 
  case Some(x: T) 
  case None 
}

Enums compile to sealed hierarchies of case classes and objects.

枚举编译为案例类和对象的密封层次结构。

sealed abstract class Option[+T] 

object  Option { 

  case class Some[+T](x: T) extends Option[T] 
  object Some { 
    def apply[T](x: T): Option[T] = Some(x) 
  } 

  val None = new Option[Nothing] { ... } }
}

Enums can be GADTs (generalized ADTs).
So cases can extend the base type with different type arguments.

枚举可以是 GADT(广义 ADT)。
所以 case 可以用不同的类型参数扩展基类型。

enum Tree[T] { 
  case True extends Tree[Boolean] 
  case False extends Tree[Boolean] 
  case IsZero(n: Tree[Int]) extends Tree[Boolean] 
  case Zero extends Tree[Int] 
  case Succ(n: Tree[Int]) extends Tree[Int] 
  case If(cond: Tree[Boolean], thenp: Tree[T], elsep: Tree[T]) extends Tree[T] 
}