使用没有方法的值类型类(value class)与使用类型别名(type alias)相比有哪些优势?

7
假设我有这个ADT:

让我们来看一下这个ADT:

case class Person(id: String)
case class Kid(id: String, name: String)

我希望以更明确、类型安全的方式表示 id 字段。我有两个选项:

1. 类型别名

type PersonId = String
case class Person(id: PersonId)
case class Kid(id: String, name: PersonId)

2. 值类型

case class PersonId(id: String) extends AnyVal
case class Person(id: PersonId)
case class Kid(id: String, name: PersonId)

哪种方法更符合惯用法?在这种情况下(没有额外的方法),使用值类有什么优势吗?
3个回答

9

类型别名只是一种语法上的方便——在某些情况下,它们可以使代码更清晰或更易于重构,但它们不提供任何额外的类型安全性。例如,假设我有这样一些代码:

type DegreesC = Double
type DegreesF = Double

def c2f(c: DegreesC): DegreesF = (c * 9.0 / 5.0) + 32

还有一个代表当前华氏温度的值:

val currentTempInF = 62.0

编译器很高兴让我将这个传递给我的方法:
scala> c2f(currentTempInF)
res1: DegreesF = 143.6

值类型类可以为您提供更多的类型安全性,而不会像案例类一样产生额外的运行时开销(虽然仍存在语法开销):

case class DegreesC(value: Double) extends AnyVal
case class DegreesF(value: Double) extends AnyVal

def c2f(c: DegreesC): DegreesF = DegreesF((c.value * 9.0 / 5.0) + 32)

val currentTempInF = DegreesF(62.0)

并且:
scala> c2f(currentTempInF)
<console>:14: error: type mismatch;
 found   : DegreesF
 required: DegreesC
       c2f(currentTempInF)
           ^

个人而言,我认为Scala中的类型别名经常被过度使用和宣传,不过我也倾向于避免使用值类,因为它们具有奇怪的限制和缺陷,而且它们所提供的运行时性能优势对我来说通常并不那么重要。无论如何,我不会说哪种方法更符合惯用法(如果有的话,我会把这种情况归为普通的、非值类的case class)。


2

对于您的具体用例(没有额外的方法)。最大的优点是类型安全。如果您声明PersonId是这样的类型

type PersonId = String

你的Kid类会取任何字符串作为它构造函数的参数。但是,如果你将PersonId声明为值类型

 case class PersonId(id: String) extends AnyVal

你的Kid类只接受PersonId作为参数,这样你就传达了这不仅仅是任何字符串,而是一个真正的PersonId。

扩展AnyVal相比普通的case类的优点在于,在大多数情况下它没有运行时开销。 你可以查看这个链接获取更多细节。


0

类型别名只提供别名,但不提供类型安全性。

type PersonId = String
case class Person(id: PersonId)

Person("some random string")

编译并且运行没有错误

case class PersonId(id: String) extends AnyVal
case class Person(id: PersonId)

Person("some random string")

在编译时由于类型错误失败


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接