How to get correct value type when extending Scala

2019-05-06 17:25发布

问题:

While there are many questions on S/O regarding difficulties with Scala Enumeration, I haven't found a question that addresses my problem. Specifically, I am trying to translate the Planet example from the Oracle Java enum docs into the Scala Enumeration idiom so as to better understand the workings and pro's & con's of the Scala form.

My ported code so far appears below and, suffice to say, it does not compile as might be expected from the principle of least surprise. Apart from type casting with .asInstanceOf, is there a better or accepted solution?

Thanks, Justin

    object Planet extends Enumeration {

  // universal gravitational constant (m3 kg-1 s-2)
  val G = 6.67300E-11

  val Mercury = Planet(3.303e+23, 2.4397e6)
  val Venus = Planet(4.869e+24, 6.0518e6)
  val Earth = Planet(5.976e+24, 6.37814e6)
  val Mars = Planet(6.421e+23, 3.3972e6)
  val Jupiter = Planet(1.9e+27, 7.1492e7)
  val Saturn = Planet(5.688e+26, 6.0268e7)
  val Uranus = Planet(8.686e+25, 2.5559e7)
  val Neptune = Planet(1.024e+26, 2.4746e7)

  case class Planet(mass: Double, radius: Double) extends Val {

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

    def surfaceWeight(otherMass: Double) = otherMass * surfaceGravity

  }

}

object PlayEnumeration extends App {


  val earthWeight = 175
  val mass = earthWeight / Planet.Earth.surfaceGravity

  Planet.values.foreach {
    // Does not compile as might be expected.
    // value surfaceWeight is not a member of play.Planet.Value
    p => println(s"Your weight on $p is ${p.surfaceWeight(mass)}")
  }

  println

}

回答1:

Try to cast enum value to Planet using asInstanceOf[Planet]

object PlanetEnum extends Enumeration {

  // universal gravitational constant (m3 kg-1 s-2)
  val G = 6.67300E-11

  val Mercury = Planet(3.303e+23, 2.4397e6)
  val Venus = Planet(4.869e+24, 6.0518e6)
  val Earth = Planet(5.976e+24, 6.37814e6)
  val Mars = Planet(6.421e+23, 3.3972e6)
  val Jupiter = Planet(1.9e+27, 7.1492e7)
  val Saturn = Planet(5.688e+26, 6.0268e7)
  val Uranus = Planet(8.686e+25, 2.5559e7)
  val Neptune = Planet(1.024e+26, 2.4746e7)

  case class Planet(mass: Double, radius: Double) extends Val {

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

    def surfaceWeight(otherMass: Double) = otherMass * surfaceGravity

  }

}

object PlayEnumeration extends App {

  val earthWeight = 175
  val mass = earthWeight / PlanetEnum.Earth.surfaceGravity

  PlanetEnum.values.foreach {
    p => println(s"Your weight on $p is ${p.asInstanceOf[Planet].surfaceWeight(mass)}")

  }

  println

}


回答2:

Add the following line to object Planet:

implicit def convert(value: Value): Planet = value.asInstanceOf[Planet]

This leverages the extraordinary power of implicits within Scala.

If you are looking for something that might give you even more flexibility (especially exhaustive pattern matching), I just posted an answer showing my general solution for enumerations within Scala.

After doing extensive research on options, the solution is a much more complete overview of this domain including solving the "sealed trait + case object" pattern where I finally solved the JVM class/object initialization ordering problem.