加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – 具有重复参数的磁铁模式(varargs)

发布时间:2020-12-16 19:16:21 所属栏目:安全 来源:网络整理
导读:是否可以使用 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: t
是否可以使用 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")
                 ^

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读