有谁知道是否有一种惯用的方式来命名 Scala 值类的内部值?假设我有一个产品 ID 的值类,是否更好地定义为:
case class ProductId(productId:String) extends AnyVal
case class ProductId(underlying:String) extends AnyVal
case class ProductId(value:String) extends AnyVal
?
这只是偏好问题还是有惯用的指导方针?
有谁知道是否有一种惯用的方式来命名 Scala 值类的内部值?假设我有一个产品 ID 的值类,是否更好地定义为:
case class ProductId(productId:String) extends AnyVal
case class ProductId(underlying:String) extends AnyVal
case class ProductId(value:String) extends AnyVal
?
这只是偏好问题还是有惯用的指导方针?
我通常喜欢使用以下模式 -
final class ProductId(val underlying: String) extends AnyVal
这有以下好处 -
underlying
您可以在需要时访问该值case class
,这有助于避免实际构造对象,因此运行时值保持为 a String
(或任何你的基础值)new
.下面的智能构造函数示例 -
final class ProductId(val underlying: String) extends AnyVal
object ProductId {
def apply(s: String): Result = {
if (s.isEmpty) {
new Failure("ProductId cannot be empty!")
} else {
new Success(new ProductId(s))
}
}
sealed trait Result
final case class Success(productId: ProductId) extends Result
final case class Failure(message: String) extends Result
}
如果要确保用户必须使用智能构造函数,请将值类的构造函数标记为私有 -
final class ProductId private (val underlying: String) extends AnyVal
如果您想确保不会意外分配 的实例ProductId
,您可以检查字节码 -
scala> :paste
class Test {
def testProductId = new ProductId("foo")
def testSmartCtor = ProductId("bar") match {
case ProductId.Success(productId) => productId
case ProductId.Failure(message) => throw new AssertionError(message)
}
}
// Ctrl+D
:javap -c ProductId$
// Skipping to the apply() method
public ProductId$Result apply(java.lang.String);
Code:
0: aload_1
1: invokevirtual #20 // Method java/lang/String.isEmpty:()Z
4: ifeq 19
7: new #22 // class ProductId$Failure
10: dup
11: ldc #24 // String ProductId cannot be empty!
13: invokespecial #27 // Method ProductId$Failure."<init>":(Ljava/lang/String;)V
16: goto 27
19: new #29 // class ProductId$Success
22: dup
23: aload_1
24: invokespecial #30 // Method ProductId$Success."<init>":(Ljava/lang/String;)V
27: areturn
字节码中没有new ProductId
引用,因此在运行时您ProductId
将被表示为String
.
请注意,如果您尝试将值类包装在使用泛型的类中(例如 Option、Either),那么您的值将被装箱。您可以通过创建一个专门用于您的值类的简单案例类来避免这种情况。虽然案例类将被实例化(因为您不能用另一个值类包装一个值类),但底层ProductId
仍将String
在运行时表示。
惯用的方法是让它成为private val
, 现在你可以了。
就标准而言,标准库更喜欢self
.
implicit final class ArrowAssoc[A](private val self: A) extends AnyVal
还有repr
,从 scaladoc forAnyVal
和repr
of 集合中回想起短语“底层运行时表示”。
class StringOps(override val repr: String) extends AnyVal with StringLike[String]
还有一些i
和n
。
就个人而言,我使用特殊标识符YMMV
.