具有多类型参数的Scala案例类

问题描述:

我需要检查嵌套模式的完整性,因此我正在编写案例类来执行此操作。我面临的主要障碍是架构可能有一个字段(如name),其中一个是StringUtf8类型,我想接受这两个实例。是否有可能避免两个案例类作为具有多类型参数的Scala案例类

case class NameValueString(name: String, value: Double) 
case class NameValueUtf8(name: Utf8, value: Double) 

和类似

case class NameValue(name @(_:String | _:Utf8), value: Double) 

上述表达式肯定失败编译。

尼基尔

+0

我想创建一个新的构造 案例类NameValueString(名称:字符串值:双人间){ 高清这个(名称:UTF8,值:双人间)=本(name.toString,值) } 有效期是你吗? – gasparms

一种方法是所谓的类型类:

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通常不会是空的,但描述你来自StringUtf8需要的任何常用功能。

您可以匹配的证据:

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

+0

很好的答案。我只想补充一点,你可以使用“上下文绑定”语法来获得相同的结果:'case class NameValue [A:StringLike](name:A,value:Double)' – stefanobaghino

+0

'implicit val utf8:StringLike [ Utf8] = new StringLike [Utf8] {}'我想? –

+0

感谢您的及时答复。我正在尝试你的建议。只有一个问题,模式匹配查询将如何? 'case x:NameValue => ...'没有模板/类型参数没有声音写入编译器:(。 –

另一种模式(不需要隐含参数):

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") 
//    ^

UPDATE

这是我看到如何基于阿列克谢的回答完毕型类方法:

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 
    } 

当你正在使用的情况下类已经,如果您只是需要不同的方法来创建它,你是在保持在只有一种方式来表示你的数据确定,您可以添加自己的应用方法以使用不同的参数启用创建。

case class NameValue(name: String, value: Double) 
object NameValue{ 
    def apply(name: Utf8, value: Double): NameValue = { 
     new NameValue(name.toString, value) 
    } 
} 

另外,如果你想模式匹配,并从不同的选项中提取NameValue,您可能需要检查脱水机,它基本上创建自己不应用方法...查看http://danielwestheide.com/blog/2012/11/21/the-neophytes-guide-to-scala-part-1-extractors.html