看看https://github.com/fthomas/refined。它允许您在类型级别细化(约束)现有类型。例如。正整数,仍然与整数有子类型关系。
语法有点冗长,它会将原语装箱(详见下文)。但除此之外,它完全符合您的要求。
这是一个简短的演示。使用细化类型定义细化和方法:
import eu.timepit.refined._
import eu.timepit.refined.api.Refined
import eu.timepit.refined.auto._
import eu.timepit.refined.numeric._
type FiveToFifteen = GreaterEqual[W.`5`.T] And Less[W.`15`.T]
type IntFiveToFifteen = Int Refined FiveToFifteen
def sum(a: IntFiveToFifteen, b: IntFiveToFifteen): Int = a + b
将它与常量一起使用(注意良好的编译错误消息):
scala> sum(5,5)
res6: Int = 10
scala> sum(0,10)
<console>:60: error: Left predicate of (!(0 < 5) && (0 < 15)) failed: Predicate (0 < 5) did not fail.
sum(0,10)
^
scala> sum(5,20)
<console>:60: error: Right predicate of (!(20 < 5) && (20 < 15)) failed: Predicate failed: (20 < 15).
sum(5,20)
^
当您有变量时,您在编译时不知道它们是否在范围内。因此,从 Int 向下转换为精炼的 int 可能会失败。在函数库中,抛出异常不被认为是好的风格。因此,refineV 方法返回一个 Either:
val x = 20
val y = 5
scala> refineV[FiveToFifteen](x)
res14: Either[String,eu.timepit.refined.api.Refined[Int,FiveToFifteen]] = Left(Right predicate of (!(20 < 5) && (20 < 15)) failed: Predicate failed: (20 < 15).)
scala> refineV[FiveToFifteen](y)
res16: Either[String,eu.timepit.refined.api.Refined[Int,FiveToFifteen]] = Right(5)