scala – 具有重复参数的磁铁模式(varargs)
是否可以使用
magnet pattern与varargs:
object Values { implicit def fromInt (x : Int ) = Values() implicit def fromInts(xs: Int*) = Values() } case class Values() object Foo { def bar(values: Values) {} } Foo.bar(0) Foo.bar(1,2,3) // ! "error: too many arguments for method bar: (values: Values)Unit" ? 解决方法
正如gourlaysama已经提到的那样,将varargs变成单个产品就可以解决这个问题,从语法上讲:
implicit def fromInts(t: Product) = Values() 这允许以下调用编译正常: Foo.bar(1,3) 这是因为编译器自动将3个参数提升为Tuple3 [Int,Int,Int].这将适用于任何数量的参数,直到22的arity.现在问题是如何使其类型安全.因为它是Product.productIterator是在方法体内返回我们的参数列表的唯一方法,但它返回Iterator [Any].我们无法保证仅使用Ints调用该方法.这应该不足为奇,因为我们实际上从未在签名中提到我们只想要Ints. 好的,所以无约束Product和vararg列表之间的关键区别在于,在后一种情况下,每个元素都是相同的类型.我们可以使用类型类对其进行编码: abstract sealed class IsVarArgsOf[P,E] object IsVarArgsOf { implicit def Tuple2[E]: IsVarArgsOf[(E,E),E] = null implicit def Tuple3[E]: IsVarArgsOf[(E,E,E] = null implicit def Tuple4[E]: IsVarArgsOf[(E,E] = null implicit def Tuple5[E]: IsVarArgsOf[(E,E] = null implicit def Tuple6[E]: IsVarArgsOf[(E,E] = null // ... and so on... yes this is verbose,but can be done once for all } implicit class RichProduct[P]( val product: P ) { def args[E]( implicit evidence: P IsVarArgsOf E ): Iterator[E] = { // NOTE: by construction,those casts are safe and cannot fail product.asInstanceOf[Product].productIterator.asInstanceOf[Iterator[E]] } } case class Values( xs: Seq[Int] ) object Values { implicit def fromInt( x : Int ) = Values( Seq( x ) ) implicit def fromInts[P]( xs: P )( implicit evidence: P IsVarArgsOf Int ) = Values( xs.args.toSeq ) } object Foo { def bar(values: Values) {} } Foo.bar(0) Foo.bar(1,3) 我们更改了方法签名表单 implicit def fromInts(t: Product) 至: implicit def fromInts[P]( xs: P )( implicit evidence: P IsVarArgsOf Int ) 在方法体内部,我们使用新的方法化args来获取我们的arg列表. 请注意,如果我们尝试使用不是Ints元组的元组调用bar,我们将收到编译错误,这会让我们恢复类型安全性. 更新:正如0__所指出的,我的上述解决方案在数字扩展方面表现不佳.换句话说,下面的代码不能编译,尽管如果bar只是采用3个Int参数它会起作用: Foo.bar(1:Short,2:Short,3:Short) Foo.bar(1:Short,2:Byte,3:Int) 要解决这个问题,我们需要做的就是修改IsVarArgsOf以便所有含义允许 abstract sealed class IsVarArgsOf[P,E] object IsVarArgsOf { implicit def Tuple2[E,X1<%E,X2<%E]: IsVarArgsOf[(X1,X2),E] = null implicit def Tuple3[E,X2<%E,X3<%E]: IsVarArgsOf[(X1,X2,X3),E] = null implicit def Tuple4[E,X3<%E,X4<%E]: IsVarArgsOf[(X1,X3,X4),E] = null // ... and so on ... } 好吧,实际上我说谎了,我们还没有完成.因为我们现在接受不同类型的元素(只要它们可以转换为公共类型,我们不能只将它们转换为期望的类型(这会导致运行时转换错误),而是我们必须应用隐式转换.我们可以像这样重做: abstract sealed class IsVarArgsOf[P,E] { def args( p: P ): Iterator[E] }; object IsVarArgsOf { implicit def Tuple2[E,X2<%E] = new IsVarArgsOf[(X1,E]{ def args( p: (X1,X2) ) = Iterator[E](p._1,p._2) } implicit def Tuple3[E,X3<%E] = new IsVarArgsOf[(X1,X3) ) = Iterator[E](p._1,p._2,p._3) } implicit def Tuple4[E,X4<%E] = new IsVarArgsOf[(X1,X4) ) = Iterator[E](p._1,p._3,p._4) } // ... and so on ... } implicit class RichProduct[P]( val product: P ) { def args[E]( implicit isVarArg: P IsVarArgsOf E ): Iterator[E] = { isVarArg.args( product ) } } 这解决了数字扩展的问题,我们在混合不相关的类型时仍然得到编译: scala> Foo.bar(1,"three") <console>:22: error: too many arguments for method bar: (values: Values)Unit Foo.bar(1,"three") ^ (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |