如何在 Scala 中向枚举添加方法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4346580/
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-10-22 02:37:18  来源:igfitidea点击:

How to add a method to Enumeration in Scala?

javascalaenumsenumeration

提问by Etam

In Java you could:

在 Java 中,您可以:

public enum Enum {
    ONE {
        public String method() {
            return "1";
        }
    },
    TWO {
        public String method() {
            return "2";
        }
    },
    THREE {
        public String method() {
            return "3";
        }
    };

    public abstract String method();
}

How do you do this in Scala?

你如何在 Scala 中做到这一点?

EDIT / Useful links:

编辑/有用的链接:

采纳答案by Etam

object Unit extends Enumeration {
  abstract class UnitValue(var name: String) extends Val(name) {
    def m: Unit
  }
  val G = new UnitValue("g") {
    def m {
        println("M from G")
    }
  }
  val KG = new UnitValue("kg") {
    def m {
        println("M from KG")
    }
  }
}

回答by Sean Ross

Here is an example of adding attributes to scala enums by extending the Enumeration.Val class.

这是通过扩展 Enumeration.Val 类向 Scala 枚举添加属性的示例。

object Planet extends Enumeration { 
   protected case class Val(val mass: Double, val radius: Double) extends super.Val { 
     def surfaceGravity: Double = Planet.G * mass / (radius * radius) 
     def surfaceWeight(otherMass: Double): Double = otherMass * surfaceGravity 
   } 
   implicit def valueToPlanetVal(x: Value) = x.asInstanceOf[Val] 

   val G: Double = 6.67300E-11 
   val Mercury = Val(3.303e+23, 2.4397e6) 
   val Venus   = Val(4.869e+24, 6.0518e6) 
   val Earth   = Val(5.976e+24, 6.37814e6) 
   val Mars    = Val(6.421e+23, 3.3972e6) 
   val Jupiter = Val(1.9e+27, 7.1492e7) 
   val Saturn  = Val(5.688e+26, 6.0268e7) 
   val Uranus  = Val(8.686e+25, 2.5559e7) 
   val Neptune = Val(1.024e+26, 2.4746e7) 
} 

scala> Planet.values.filter(_.radius > 7.0e6) 
res16: Planet.ValueSet = Planet.ValueSet(Jupiter, Saturn, Uranus, Neptune) 

回答by Aaron Novstrup

Building on Chris' solution, you can achieve somewhat nicer syntax with an implicit conversion:

基于Chris 的解决方案,您可以通过隐式转换实现更好的语法:

object Suit extends Enumeration {
   val Clubs, Diamonds, Hearts, Spades = Value

   class SuitValue(suit: Value) {
      def isRed = !isBlack
      def isBlack = suit match {
         case Clubs | Spades => true
         case _              => false
      }
   }

   implicit def value2SuitValue(suit: Value) = new SuitValue(suit)
} 

Then you can call, for example, Suit.Clubs.isRed.

然后你可以调用,例如,Suit.Clubs.isRed

回答by soc

Scala enumerationsare distinct from Java enumerations.

Scala枚举不同于 Java 枚举。

At the moment, there is no way add methods to it (in a sane way). There are some work-arounds, but nothing which works in all cases anddoesn't look like syntactic garbage.

目前,没有办法向它添加方法(以一种理智的方式)。有一些变通方法,但没有什么在所有情况下都有效并且看起来不像语法垃圾。

I tried something similiar (adding methods to enumerated instance of a class, while being able to create new instances at runtime and having a working equivalence relationship between the objects and the newinstances of the class), but was stopped by bug #4023("getClasses/getDeclaredClasses seems to miss some (REPL) or all (scalac) classes (objects) declared").

我尝试了一些类似的方法(向类的枚举实例添加方法,同时能够在运行时创建新实例并在objects 和new类的实例之间具有工作等价关系),但被错误#4023(“getClasses /getDeclaredClasses 似乎错过了一些(REPL)或所有(scalac)类(对象)声明”。

Have a look at these related questions by me:

看看我这些相关的问题:

Honestly, I wouldn't use Enumeration. This is a class originating from Scala 1.0 (2004) and it has weird stuff in it and not many people (except those who have written it) understands how to use it without an tutorial first.

老实说,我不会使用Enumeration. 这是一个源自 Scala 1.0 (2004) 的类,其中包含一些奇怪的东西,并且没有多少人(除了编写它的人)了解如何在没有教程的情况下使用它。

If I would absolutely need an enumeration I would just write that class in Java.

如果我绝对需要枚举,我只会用 Java 编写该类。

回答by missingfaktor

If you don't need to iterate over enum values or do some other enum-ish stuff, I'd advise using ADTs instead of Enumeration.

如果您不需要迭代枚举值或执行其他一些枚举类型的东西,我建议您使用 ADT 而不是Enumeration.

sealed abstract class Enum {
  def method: String = this match {
    case One => "1"
    case Two => "2"
    case Three => "3"
  }
}
case object One extends Enum
case object Two extends Enum
case object Three extends Enum

This approach has one advantage over Enumerationthat compiler will warn you when you forget one or more cases in the matchexpression.

Enumeration当您忘记match表达式中的一个或多个 case 时编译器会警告您相比,这种方法有一个优点。

回答by ozeebee

Elaborating on Aaron's solution, an even more compact form in Scala 2.10, using implicit classes:

详细说明Aaron 的解决方案,这是 Scala 2.10 中更紧凑的形式,使用隐式类

object Suit extends Enumeration {
   val Clubs, Diamonds, Hearts, Spades = Value

   implicit class SuitValue(suit: Value) {
      def isRed = !isBlack
      def isBlack = suit match {
         case Clubs | Spades => true
         case _              => false
      }
   }
} 

and then you can use it like this: Suit.Clubs.isRed

然后你可以像这样使用它: Suit.Clubs.isRed

回答by oxbow_lakes

You can do this:

你可以这样做:

object Suit extends Enumeration {
  val Clubs, Diamonds, Hearts, Spades = Value

  def isRed(suit : Value) = !isBlack(suit)
  def isBlack(suit : Value) = suit match {
    case Clubs | Spades => true
    case _              => false
  }
}

Obviously this is not perfect but you can then do:

显然这并不完美,但您可以这样做:

Suit.isBlack(Suit.Clubs)

回答by Jan van der Vorst

Scala's Enumeration does not allow properties and/or methods to be added to values in your enumeration. With this new MyEnumeration you can.

Scala 的枚举不允许将属性和/或方法添加到枚举中的值。有了这个新的 MyEnumeration,你就可以了。

abstract class MyEnumeration {
  // "Value" must be the name of the class defining your values type Value
  type Value

  // Contains your values in definition order
  private val vals = collection.mutable.LinkedHashMap[String, Value]()

  // A mixin for your values class to automatically collect the values
  protected trait ValuesCollector { self: Value =>
    private val ordinal = vals.size

    vals += (fieldNames(ordinal) -> self)

    def getName = fieldNames(ordinal)
    override def toString = getName
  }

  def apply(ordinal: Int) = vals(fieldNames(ordinal))
  def apply(fldName: String) = vals(fldName)

  def values = vals.values
  def namedValues: collection.Map[String, Value] = vals

  // Getting the field names through reflection.
  // Copied from scala.Enumeration
  private val fieldNames = getClass.getMethods filter (m =>
    m.getParameterTypes.isEmpty &&
    classOf[ValuesCollector].isAssignableFrom(m.getReturnType) &&
    m.getDeclaringClass != classOf[MyEnumeration]) map (_.getName)

}

Here you see the Planet example in Scala.

在这里您可以看到 Scala 中的 Planet 示例。

object Planet extends MyEnumeration {

  case class Value(val mass: Double, val radius: Double) extends ValuesCollector {
    // universal gravitational constant  (m3 kg-1 s-2)
    private val G = 6.67300E-11;

    def surfaceGravity = G * mass / (radius * radius)
    def surfaceWeight(otherMass: Double) = otherMass * surfaceGravity

  }

  val MERCURY = Value(3.303e+23, 2.4397e6)
  val VENUS = Value(4.869e+24, 6.0518e6)
  val EARTH = Value(5.976e+24, 6.37814e6)
  val MARS = Value(6.421e+23, 3.3972e6)
  val JUPITER = Value(1.9e+27, 7.1492e7)
  val SATURN = Value(5.688e+26, 6.0268e7)
  val URANUS = Value(8.686e+25, 2.5559e7)
  val NEPTUNE = Value(1.024e+26, 2.4746e7)
  val PLUTO = Value(1.27e+22, 1.137e6)

}

object PlanetTest {
  def main(args: Array[String]) {
    val earthWeight = 175
    val mass = earthWeight/Planet.EARTH.surfaceGravity
    for (p <- Planet.values) println("Your weight on %s is %f" format (p, p.surfaceWeight(mass)))
    /* Your weight on MERCURY is 66.107583
     * Your weight on VENUS is 158.374842
     * Your weight on EARTH is 175.000000
     * Your weight on MARS is 66.279007
     * Your weight on JUPITER is 442.847567
     * Your weight on SATURN is 186.552719
     * Your weight on URANUS is 158.397260
     * Your weight on NEPTUNE is 199.207413
     * Your weight on PLUTO is 11.703031
     */
  }

} 

回答by sourcedelica

If you absolutely need to have methods per enumeration value andneed to be able to iterate over the values, you can do something like this:

如果您绝对需要每个枚举值都有方法并且需要能够迭代这些值,您可以执行以下操作:

object BatchCategory extends Enumeration {
  class BatchCategory extends Val {
    val isOfficial, isTest, isUser = false
  }

  val OFFICIAL = new BatchCategory { override val isOfficial = true }
  val TEST =     new BatchCategory { override val isTest = true }
  val USER =     new BatchCategory { override val isUser = true }

  // Needed to get BatchCategory from Enumeration.values
  implicit def valueToBatchCategory(v: Value): BatchCategory = v match {
    case bc: BatchCategory => bc
    case x => throw new IllegalArgumentException("Value is not a BatchCategory: " + x)
  }

  def valueOf(catStr: String): BatchCategory = {
    BatchCategory.values.
      find { v => val s = v.toString; s.take(1) == catStr || s == catStr }.
      getOrElse(throw new IllegalArgumentException("Unknown category '" + catStr + "' !  "))
  }

  def main(args: Array[String]) {
    BatchCategory.values.foreach(v => println(v + " isOfficial=" + v.isOfficial))
  }
}

prints

印刷

OFFICIAL isOfficial=true
TEST isOfficial=false
USER isOfficial=false

This was done for some legacy code that couldn't be moved to a saner enum strategy besides Enumeration.

这是为一些遗留代码完成的,除了枚举之外,这些代码无法移动到更合理的枚举策略中。

回答by Shiva Wu

After checking out the source code of scala.Enumeration, I got this:

在查看了 scala.Enumeration 的源代码后,我得到了这个:


object MyEnum extends Enumeration {
  val ONE = new Val { def method = "1" }
  val TWO = new Val { def method = "2" }
  val THREE = new Val { def method = "3" }
}

It seems hard to get rid of the 'new' since anonymized class is used. If anyone knows how to do it, let me know :)

由于使用了匿名类,因此似乎很难摆脱“新”。如果有人知道怎么做,请告诉我:)