为什么Scala隐式转换在这里使用两个args而不是一个?

时间:2011-05-03 14:03:50

标签: scala

在对象ValueSet上,我有apply(pairs : (String, ValueBase)*)和从Int和String到ValueBase的隐式转换。如果我应用ValueSet("a" -> 1, "b" -> 2),那么(String, Int)对会转换为(String, ValueBase)并且工作正常。如果我只使用一对ValueSet("a" -> 1),那么它表示(String,Int)没有超载申请,即它不会隐式转换。我可以通过添加apply[V <% ValueBase](p : (String, V))来解决这个问题。apply(pairs : (String, ValueBase)*)适用于一对案例。

为什么class ValueBase case class ValueInt(val value : Int) extends ValueBase case class ValueString(val value : String) extends ValueBase case class ValuePair(val key : String, val value : ValueBase) case class ValueSet(val value : List[ValuePair]) extends ValueBase object ValueSet { def apply(pairs : (String, ValueBase)*) : ValueSet = { ValueSet(pairs.map(p => ValuePair(p._1, p._2)).toList) } /* Commenting out this apply() means single-pair * ValueSet("a" -> 1) * will not compile, error is: * overloaded method value apply with alternatives: (value: List[ValuePair])ValueSet <and> (pairs: (String, ValueBase)*)ValueSet cannot be applied to ((java.lang.String, Int)) * Why does (String,Int) implicit convert to (String,ValueBase) if there are two args but not if there's one? * Why do I need this apply()? */ def apply[V <% ValueBase](p : (String, V)) : ValueSet = { ValueSet(List(ValuePair(p._1, p._2))) } } object Sample { implicit def int2value(i : Int) = ValueInt(i) implicit def string2value(s : String) = ValueString(s) /* These samples show the goal, to construct the sets * in a nice Map-literal sort of style */ val oneInt = ValueSet("a" -> 1) val oneString = ValueSet("b" -> "c") val twoInt = ValueSet("d" -> 2, "e" -> 3) val twoTypes = ValueSet("f" -> 4, "g" -> "quick brown fox") /* Taking ArrowAssoc out of the picture and typing "Pair" * explicitly doesn't seem to matter */ val oneInt2 = ValueSet(Pair("a", 1)) val twoTypes2 = ValueSet(Pair("f", 4), Pair("g", "quick brown fox")) } 只能使用一对?

(额外的问题:添加额外的apply()似乎可以解决问题 - 是否有更好的解决方案?这个解决方案有什么问题吗?)

这是一个完整的可编译示例,简化了我的实际代码,试图显示最小问题。

{{1}}

1 个答案:

答案 0 :(得分:4)

正如Daniel Sobral在评论中所解释的那样,这种情况发生了,因为“编译器看到一个参数,两个应用可能需要一个参数的方法,而且传递的参数都不适合。然后放弃考虑其他任何事情,因为它不知道尝试哪种方法。如果传递了两个参数,则丢弃一个apply,编译器会查找使另一个参数生效的隐式转换。“

(请记住,编译器会自动定义一个apply,因为您正在定义case class。)

如果你改为编写,隐式转换就可以了:

object ValueSet {
  def fromPairs(pairs: (String, ValueBase)*): ValueSet = {
    ValueSet(pairs.map(p => ValuePair(p._1, p._2)).toList)
  }
}

object Sample {
  implicit def int2value(i: Int): ValueInt = ValueInt(i)
  implicit def string2value(s: String): ValueString = ValueString(s)

  /* These samples show the goal, to construct the sets
   * in a nice Map-literal sort of style
   */
  val oneInt = ValueSet.fromPairs("a" -> 1)
  val oneString = ValueSet.fromPairs("b" -> "c")
  val twoInt = ValueSet.fromPairs("d" -> 2, "e" -> 3)
  val twoTypes = ValueSet.fromPairs("f" -> 4, "g" -> "quick brown fox")
}

不完全是你所希望的,我知道......