价值验证 类
validations in value classes
SIP-15 意味着可以使用值 classes 来定义新的数字 classes,例如正数。是否可以编写这样的约束,即在没有构造函数的情况下底层 > 0 而不必调用单独的方法来验证约束(即;创建此类 class 的有效实例是简洁的)?
如果值 classes 具有构造函数的概念,那么可以在此处进行如下所示的验证,但不支持(即,以下代码无法编译)
implicit class Volatility(val underlying: Double) extends AnyVal {
require(!underlying.isNaN && !underlying.isInfinite && underlying > 0, "volatility must be a positive finite number")
override def toString = s"Volatility($underlying)"
}
Volatility(-1.0) //should ideally fail
隐式转换为标记为已通过运行时要求的类型。
scala> trait Pos
defined trait Pos
scala> implicit class P(val i: Int with Pos) extends AnyVal { def f = i }
defined class P
scala> implicit def cv(i: Int): Int with Pos = { require(i>0); i.asInstanceOf[Int with Pos] }
warning: there was one feature warning; re-run with -feature for details
cv: (i: Int)Int with Pos
scala> new P(42).f
res0: Int with Pos = 42
scala> :javap -prv -
17: invokevirtual #35 // Method $line5/$read$$iw$$iw$.cv:(I)I
20: invokevirtual #38 // Method $line4/$read$$iw$$iw$P$.f$extension:(I)I
scala> new P(-42).f
java.lang.IllegalArgumentException: requirement failed
at scala.Predef$.require(Predef.scala:207)
at .cv(<console>:13)
... 33 elided
您还可以拥有强制不变量的私有方法。
scala> implicit class P(val i: Int with Pos) extends AnyVal { private def g = require(i>0) ; def f = { g; i } }
defined class P
scala> new P(-42.asInstanceOf[Int with Pos]).f
java.lang.IllegalArgumentException: requirement failed
at scala.Predef$.require(Predef.scala:207)
at P$.$line10$$read$P$$g$extension(<console>:14)
at P$.f$extension(<console>)
... 33 elided
您可以使用 refined 通过使用精化的 Positive
谓词精化您的 Double
来提升编译时间的验证步骤:
import eu.timepit.refined.auto._
import eu.timepit.refined.numeric._
import shapeless.tag.@@
scala> implicit class Volatility(val underlying: Double @@ Positive) extends AnyVal
defined class Volatility
scala> Volatility(1.5)
res1: Volatility = Volatility@3ff80000
scala> Volatility(-1.5)
<console>:52: error: Predicate failed: (-1.5 > 0).
Volatility(-1.5)
^
请注意,最后一个错误是编译错误,而不是运行时错误。
我完成此操作的方法是使用伴随对象的 .apply
方法在调用案例 class 的 private
构造函数之前添加 require
约束"instantiating" 值。
警告: 下面的代码不会在 REPL/Scala 工作表中编译。扩展 AnyVal 的案例 class 必须是顶级 class;即不能嵌套在另一个 class、特征或对象的范围内。 REPL 和 Scala Worksheet 都是通过在执行前将所有代码推入一个包含 class 的不可见的
来实现的。
object PositiveInt {
def apply(value: Int): PositiveInt = {
require(value >= 0, s"value [$value] must be greater than or equal to 0")
new PositiveInt(value)
}
}
case class PositiveInt private(value: Int) extends AnyVal
val positiveTestA = PositiveInt(0)
val positiveTestB = PositiveInt(1)
val positiveTestC = PositiveInt(-1) //throws required exception
这对您的用例有用吗?将构造函数设为私有并使用带有验证逻辑的伴随对象来创建新实例。
class User private (val userIdentifier:String) extends AnyVal {}
object User {
def apply(userIdentifier: String): User = {
if(Option(userIdentifier).exists(_.trim.isEmpty)) throw new IllegalArgumentException("User identifier cannot be empty!")
new User(userIdentifier)
}
}
SIP-15 意味着可以使用值 classes 来定义新的数字 classes,例如正数。是否可以编写这样的约束,即在没有构造函数的情况下底层 > 0 而不必调用单独的方法来验证约束(即;创建此类 class 的有效实例是简洁的)?
如果值 classes 具有构造函数的概念,那么可以在此处进行如下所示的验证,但不支持(即,以下代码无法编译)
implicit class Volatility(val underlying: Double) extends AnyVal {
require(!underlying.isNaN && !underlying.isInfinite && underlying > 0, "volatility must be a positive finite number")
override def toString = s"Volatility($underlying)"
}
Volatility(-1.0) //should ideally fail
隐式转换为标记为已通过运行时要求的类型。
scala> trait Pos
defined trait Pos
scala> implicit class P(val i: Int with Pos) extends AnyVal { def f = i }
defined class P
scala> implicit def cv(i: Int): Int with Pos = { require(i>0); i.asInstanceOf[Int with Pos] }
warning: there was one feature warning; re-run with -feature for details
cv: (i: Int)Int with Pos
scala> new P(42).f
res0: Int with Pos = 42
scala> :javap -prv -
17: invokevirtual #35 // Method $line5/$read$$iw$$iw$.cv:(I)I
20: invokevirtual #38 // Method $line4/$read$$iw$$iw$P$.f$extension:(I)I
scala> new P(-42).f
java.lang.IllegalArgumentException: requirement failed
at scala.Predef$.require(Predef.scala:207)
at .cv(<console>:13)
... 33 elided
您还可以拥有强制不变量的私有方法。
scala> implicit class P(val i: Int with Pos) extends AnyVal { private def g = require(i>0) ; def f = { g; i } }
defined class P
scala> new P(-42.asInstanceOf[Int with Pos]).f
java.lang.IllegalArgumentException: requirement failed
at scala.Predef$.require(Predef.scala:207)
at P$.$line10$$read$P$$g$extension(<console>:14)
at P$.f$extension(<console>)
... 33 elided
您可以使用 refined 通过使用精化的 Positive
谓词精化您的 Double
来提升编译时间的验证步骤:
import eu.timepit.refined.auto._
import eu.timepit.refined.numeric._
import shapeless.tag.@@
scala> implicit class Volatility(val underlying: Double @@ Positive) extends AnyVal
defined class Volatility
scala> Volatility(1.5)
res1: Volatility = Volatility@3ff80000
scala> Volatility(-1.5)
<console>:52: error: Predicate failed: (-1.5 > 0).
Volatility(-1.5)
^
请注意,最后一个错误是编译错误,而不是运行时错误。
我完成此操作的方法是使用伴随对象的 .apply
方法在调用案例 class 的 private
构造函数之前添加 require
约束"instantiating" 值。
警告: 下面的代码不会在 REPL/Scala 工作表中编译。扩展 AnyVal 的案例 class 必须是顶级 class;即不能嵌套在另一个 class、特征或对象的范围内。 REPL 和 Scala Worksheet 都是通过在执行前将所有代码推入一个包含 class 的不可见的
来实现的。object PositiveInt {
def apply(value: Int): PositiveInt = {
require(value >= 0, s"value [$value] must be greater than or equal to 0")
new PositiveInt(value)
}
}
case class PositiveInt private(value: Int) extends AnyVal
val positiveTestA = PositiveInt(0)
val positiveTestB = PositiveInt(1)
val positiveTestC = PositiveInt(-1) //throws required exception
这对您的用例有用吗?将构造函数设为私有并使用带有验证逻辑的伴随对象来创建新实例。
class User private (val userIdentifier:String) extends AnyVal {}
object User {
def apply(userIdentifier: String): User = {
if(Option(userIdentifier).exists(_.trim.isEmpty)) throw new IllegalArgumentException("User identifier cannot be empty!")
new User(userIdentifier)
}
}