如何解决方法选择中的歧义?

时间:2013-03-24 02:11:53

标签: scala

为了在Scala 2.10中尝试新的值类,我想我会按照Ruby提供的方式向Int添加一个times方法,这样可以轻松地重复几次代码块。所以我写了这个:

object Test extends App {
  implicit class Int_times( val n:Int ) extends AnyVal {
    def times( f:    => Any ) { var i = n; while ( i>0 ) { f   ; i -= 1 } }  // A
    def times( f:Int => Any ) { var i = n; while ( i>0 ) { f(i); i -= 1 } }  // B
  }
  3 times   print(1)  ; println                                 // 111 ?
  3 times { print(_) }; println                                 // 321 ?
  var x = 0                ; 3 times { x += 1   }; println(x)   // 3   ?
  var s = new StringBuilder; 3 times { s += 'x' }; println(s)   // xxx ?
}

如果你遇到困惑,你可能会问自己,上面会产生什么输出。它产生我期望的输出(在上面的评论中)。

正如您所看到的,有两种方法可以使重复执行的代码可以使用索引。在我们进入StringBuilder测试之前,输出看起来很好:

111
321
3
java.lang.StringIndexOutOfBoundsException: String index out of range: 3

部分了解发生了什么,但我有一些问题。到目前为止我已经想到的是,虽然我认为3 times { s += 'x' }会导致times的版本A被调用,但事实上版本B被调用了。事实上,如果我删除B(并注释掉第二个测试),则会在最后一次测试中调用A并且工作正常。

我猜测{ s += 'x' }含糊不清,可以解释为满足=> Any的代码块,也可以解释为评估后的结果的表达式{ {1}}。也就是说,Int => Any返回StringBuilder本身,而StringBuilder有一个s += 'x'方法,允许您获取特定字符。编译器在调用apply( ix:Int )之前执行后一种解释并执行块中的代码,然后将结果(StringBuilder本身)传递给times,然后尝试三次索引。由于StringBuilder此时只有一个字符,times会导致异常。

首先,有人可以确认/纠正/阐明上述内容吗?用于描述这个的正确术语是什么?

其次,这里有什么合适的解决方案?我唯一想到的就是通过重命名其中一种方法来避免歧义。

1 个答案:

答案 0 :(得分:2)

正确,scala的Stringbuilder上的apply方法返回索引处的字符。调用此方法是因为Stringbuilder可以强制转换为f:Int => Any,例如以下内容将编译:

val s2: (Int => Any) = s += 'x'

为了解决这个问题,我能想到的最好方法是显式输入匿名函数:

3 times { _:Any => s += 'x' };