我需要检查嵌套模式的完整性,因此我正在编写案例类来执行此操作。我面临的主要障碍是架构可能包含name
或String
类型的字段(例如,Utf8
),我想接受这两个实例。是否可以避免使用两个案例类
case class NameValueString(name: String, value: Double)
case class NameValueUtf8(name: Utf8, value: Double)
等等
case class NameValue(name @(_:String | _:Utf8), value: Double)
上面的表达式肯定无法编译。
尼基尔
答案 0 :(得分:3)
一种方法是所谓的类型类:
trait StringLike[A] // sealed if you don't want anybody to implement it elsewhere
object StringLike {
implicit object StringEv extends StringLike[String] {}
implicit object Utf8Ev extends StringLike[Utf8] {}
}
case class NameValue[A](name: A, value: Double)(implicit val stringLike: StringLike[A])
当然,StringLike
通常不会为空,但会描述String
和Utf8
所需的常用功能。
您可以匹配证据:
def nameLength[A](nameValue: NameValue[A]) = nameValue.stringLike match {
case StringLike.StringEv =>
nameValue.name.length // calls String#length
case StringLike.Utf8Ev =>
nameValue.name.length // calls Utf8#length (assuming Utf8 has such method)
}
在这种情况下,编译器甚至会知道A
(以及nameValue.name
的类型)在第一个分支中是String
而在第二个分支中是Utf8
。
答案 1 :(得分:1)
另一种模式(不需要隐式参数):
import scala.language.implicitConversions
class StringLike[A](name: A) {
override def toString = {
name match {
case s: String => s"String: $s"
case i: Int => s"Int: $i"
}
}
}
implicit def string2StringLike(s: String) = new StringLike(s)
implicit def int2StringLike(i: Int) = new StringLike(i)
case class NameValue[A](name: StringLike[A], value: String) {
override def toString = name.toString
}
NameValue("123", "123")
//> NameValue[String] = String: 123
NameValue(13, "123")
//> NameValue[Int] = Int: 13
NameValue(13.9, "123")
// error: type mismatch;
// found : Double(13.9)
// required: StringLike[?]
// NameValue(13.9, "123")
// ^
<强>更新强>
以下是我根据Alexey的回答看到完整的类型类方法:
trait StringLike[A] {
def toString(x: A): String
}
object StringLike {
implicit object StringStringLike extends StringLike[String] {
def toString(s: String) = s"String: $s"
}
implicit object IntStringLike extends StringLike[Int] {
def toString(i: Int) = s"Int: $i"
}
}
import StringLike._
case class NameValue[A](name: A, value: Double)(implicit ev: StringLike[A]) {
override def toString = ev.toString(name)
}
NameValue(1, 2.0)
//> NameValue[Int] = Int: 1
NameValue("123", 2.0)
//> NameValue[String] = String: 123
NameValue(2.0, 2.0)
// error: could not find implicit value for parameter ev:
// StringLike[Double]
// NameValue(2.0, 2.0)
// ^
<强> UPDATE2 强>
另外一个(使用union type进行类型安全):
type ¬[A] = A => Nothing
type ¬¬[A] = ¬[¬[A]]
type ∨[T, U] = ¬[¬[T] with ¬[U]]
type |∨|[T, U] = { type λ[X] = ¬¬[X] <:< (T ∨ U) }
def nameLength[A: ClassTag: (Int |∨| String)#λ](nameValue: NameValue[A]) =
nameValue.name match {
case s:String => s.length
case i:Int => i + 1
}
答案 2 :(得分:0)
由于您已经使用了案例类,如果您只需要不同的方法来创建它,并且您可以仅以一种方式保持数据表示,则可以添加自己的apply方法以使用不同的参数启用创建。
case class NameValue(name: String, value: Double)
object NameValue{
def apply(name: Utf8, value: Double): NameValue = {
new NameValue( name.toString, value )
}
}
或者,如果您想模式匹配并从不同的选项中提取NameValue,您可能需要检查Extractors,它基本上是创建您自己的unapply方法... check http://danielwestheide.com/blog/2012/11/21/the-neophytes-guide-to-scala-part-1-extractors.html