What do user-defined value classes look like from

2019-02-21 20:09发布

问题:

I think I understand the new "value class" feature of Scala 2.10, by comparison with Haskell's newtype:

trait BoundedValue[+This] extends Any { this: This =>

  def upperBound: This

  def lowerBound: This

}

class Probability @throws(classOf[IllegalArgumentException]) (v: Double) extends AnyVal with BoundedValue[Probability] {

  val value: Double = if ((v >= 0.0) && (v <= 1.0)) v else throw new IllegalArgumentException((v.toString) + "is not within the range [0.0, 1.0]")

  override val upperBound: Probability = new Probability(0.0)

  override val lowerBound: Probability = new Probability(1.0)

  // Implement probability arithmetic here;
  // will be represented by Double at runtime.

}

The question I have is, how does a value class appear to Java code that uses the Scala package in which it is declared? Does the value class appear as a reference class from the Java side, or is it erased completely (and thus appears as the type it wraps)? In other words, how type-safe are value classes when Java is involved on the source level?


EDIT

The code above won't compile, according to the SIP-15 document (linked in Daniel's answer), because value classes aren't allowed to have any initialization logic, because either v must be explicitly a val or Probability must have an unbox method and a corresponding box method on it's companion object, and because value classes must have exactly one field. The correct code is:

trait BoundedValue[This <: BoundedValue[This]] extends Any { this: This =>

  def upperBound: This

  def lowerBound: This

}

class Probability private[Probability] (value: Double) extends AnyVal with BoundedValue[Probability] {

  @inline override def upperBound: Probability = new Probability(0.0)

  @inline override def lowerBound: Probability = new Probability(1.0)

  @inline def unbox: Double = value

  // Implement probability arithmetic here;
  // will be represented by Double at runtime (mostly).

}

object Probability {

  @throws(classOf[IllegalArgumentException])
  def box(v: Double): Probability = if ((v >= 0.0) && (v <= 1.0)) new Probability(v) else throw new IllegalArgumentException((v.toString) + "is not within the range [0.0, 1.0]")

}

The question itself is still valid as is, however.

回答1:

Value classes compile as normal classes, and may well appear as references.

The magic in them is that, when the value class doesn't escape the scope, all traces of it are erased from code, effectively inlining all the code. And, of course, giving additional type safety.

See also SIP-15, which explains the mechanics.