2011-05-03 80 views
3

在对象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))来解决这个问题,它可以在一对情况下工作。为什么Scala隐式转换工作有两个参数,但没有一个?

为什么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")) 
} 

回答

4

出现这种情况,由丹尼尔·索布拉尔在评论解释说,因为“编译器看到一个说法,两名申请这可能需要一个参数的方法,并且参数传递配合都不是。然后放弃考虑其他任何事情,因为它不知道要尝试哪种方法。如果传递了两个参数,则丢弃一个应用并且编译器查找使另一个工作的隐式转换。“

(请记住,编译器会自动定义一个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") 
} 

不正是你所希望看到的,我知道...

+0

有趣。我不明白为什么编译器定义的apply()会混淆这个: - /它似乎不像Pair(“a” - > 1)应该有任何方式成为List [ValuePair],所以编译器定义不应该使用apply()...是否给出两个single-arg apply()重载的问题,编译器会放弃隐式转换?但是为什么添加额外的apply()修复它 - 可能是因为它不需要隐式转换来匹配超载,这是由于“<%”? – 2011-05-03 14:31:04

+0

@Havoc Jean-Philippe将它击中。这里的问题是编译器会看到一个参数,两个'apply'方法可能需要一个参数,并且传递的参数都不适合。它然后*放弃*考虑其他任何事情,因为它不知道要尝试哪种方法。如果传递了两个参数,那么放弃一个'apply'并且编译器寻找使另一个工作的隐式转换。 @Jean,你能用这些信息补充答案吗? – 2011-05-03 14:57:29

+0

@浩瀚我不完全知道。 Scala规范重载分辨率的页面并不是一个简单的部分。不知道为什么额外的'apply'修复它,但请记住'T <%S'的视图是代表从'T'到'S'转换的隐式参数的缩写,所以它也是在编译时需要隐式查找。 – 2011-05-03 14:57:39

相关问题