我想定义一个由通用Product类型参数化的函数,但它可以解决Product的问题.这是一个示例代码段.我想在调用f(…)时进行arity检查,而不是在调用f(…)()时进行.我怎样才能做到这一点?
def f[T<:Product](names:Seq[String],values:()=>T) = { () => { val x = values() if (x.productArity != names.size) scala.sys.error("Size mismatch") names.zip(x.productIterator.map(_.toString).toSeq).map(kv => kv._1+"="+kv._2) } }
(这是一个非常无用的功能,仅用于演示.重要的一点是(1)它是按产品类型参数化的,(2)只有当产品的arity与我知道的某个值匹配时,该功能才有意义.调用函数,(3)当我调用函数时,我很难/不可能获得Product的实例.我的实际用例是基于spark RDD写出SQL语句的实用程序类.)
如果有必要,我可以写出一整套函数,每个函数对应一个元组.但这感觉很讨厌,我希望有更好的解决方案.
解决方法
使用类型类可以找到比编写不同方法更好的方法:
case class Arity[P]( get: Int ) object Arity { def apply[P]( implicit arity: Arity[P] ) = arity implicit def tuple2[A,B] = Arity[(A,B)]( 2 ) implicit def tuple3[A,B,C] = Arity[(A,C)]( 3 ) //... } def f[T<:Product:Arity](names:Seq[String],values:()=>T) = { () => { val x = values() if ( Arity[T].get != names.size) scala.sys.error("Size mismatch") names.zip(x.productIterator.map(_.toString).toSeq).map(kv => kv._1+"="+kv._2) } }
当然,您需要为所有可能的元组大小写下Arity对象.您可以使用代码生成或使用宏(如果您是大胆和耐心)自动化它.