什么是Scala doc中的“Shadowed Implicit Value Members”?

时间:2013-10-27 07:49:24

标签: scala

StringOps doc中有 Shadowed Implicit Value Members 部分。 E.g:

  

def split(arg0:String,arg1:Int):Array [String]

     

隐含信息
      通过scala.Predef中的方法unaugmentString执行从StringOps到String的隐式转换添加此成员。

     

如影随形
      此隐式继承的成员被此类中的一个或多个成员遮蔽。       要访问此成员,您可以使用类型归属:

(stringOps: String).split(arg0, arg1)
     

定义类
      串

但是当我尝试运行以下程序时:

"aaa bbb ccc".split(" ", 2) //> res0: Array[String] = Array(aaa, bbb ccc)

调用String.split(arg0: String, arg1: Int)不需要像在描述的文档中那样使用类型的归档

那么 Shadowed Implicit Value Members 指的是什么?我试过问谷歌但找不到任何参考。

是这样的:

class A {
  def foo() = println("foo")
}

class AOps(a: A) {
  def bar() = println("bar")
  def foo() = println("new foo")
  def foo(i: Int) = println("foo %d".format(i))
}

object Program {
  implicit def A2AOps(a: A) = new AOps(a)         //> A2AOps: (a: A)AOps

  val a = new A()                                 //> a  : A = A@15669ae
  a.foo                                           //> foo
  a.bar                                           //> bar
  (a: AOps).foo                                   //> new foo
  a.foo(1)                                        //> foo 1
}

然后String.split(...)StringOps.split函数签名不同,因此不需要“类型归属”。

这是“Shadowed Implicit Value Members”的缩写吗?我有点困惑。谢谢!

4 个答案:

答案 0 :(得分:6)

通常情况下,当您调用对于给定类型不存在的方法时,Scala编译器将执行隐式转换:

case class Foo(x :String)

implicit class Bar(foo: Foo) {
  def barOnly = "w00p"
}

println( Foo("test").barOnly )

当我们在barOnly的实例上调用方法Foo时,scala编译器可以看到它需要执行从FooBar的隐式转换以便为我们提供使用该方法,我们得到w00p的预期输出。 Try it

但是如果FooBar中存在具有相同签名的方法,那么我们会有一些阴影,并且scala编译器不会进行隐式转换,除非我们明确要求使用类型ascription:

case class Foo(x :String) {
  def whoAmI = "Foo: " + x
}

implicit class Bar(foo: Foo) {
  def whoAmI = "Bar: " + foo.x
}

println( Foo("test").whoAmI )
println( (Foo("test"): Bar).whoAmI )

输出结果为:

Foo: test
Bar: test

Try it

在scaladocs中split的示例中,splitString上都有StringOps个方法,但它们采用不同的参数类型,所以我&#39 ;我不完全确定为什么文档警告我们必须使用类型归属。在这种情况下,我们不需要为编译器消除歧义,类型ascription也没有效果:

import scala.collection.immutable.StringOps

val stringOps: StringOps = "aaa bbb ccc"

println( stringOps.split("a", 2).mkString )
println( (stringOps: String).split("a", 2).mkString )

这两行的输出完全相同:

aa bbb ccc
aa bbb ccc

Try it

可能只是文档中的错误。

答案 1 :(得分:0)

“aaa bbb ccc”是一个String对象,而不是StringOps对象,因此注释无关紧要。

这是一个极端情况,你可能永远不会显式创建一个StringOps对象,只依赖于隐式转换,所以这个Scaladoc注释似乎没用。

答案 2 :(得分:0)

scaladoc告诉您可以在StringOps实例上调用的非StringOps方法。调用此方法将触发从StringOps到String的隐含转换。

我们几乎不使用原始的StringOps对象,因此在实践中不太可能进行隐式转换。

解释类型归属in this Stack Overflow question

scala> val so=new scala.collection.immutable.StringOps("aaa bbb ccc")
so: scala.collection.immutable.StringOps = aaa bbb ccc

scala> so.split(" ",2)
res8: Array[String] = Array(aaa, bbb ccc)

scala> 

答案 3 :(得分:-1)

当你有一个使用不同签名重载方法的隐式类时,看起来类型归属是必要的。这与上面的其他答案所说的相冲突。

例如,使用apply的{​​{1}}方法。假设我想用一个采用零索引整数位置的方法重载它,所以我可以索引到TreeMap,好像它是一个数组:

TreeMap

你会注意到最后一行失败了。这里发生的是2被转换为double,然后用 implicit class MapIndexerExtension(m : TreeMap[Double,String]) { def apply(idx : Int) = m.take(idx + 1).last } val m = TreeMap( 0.3333333 -> "A third", 2.71828 -> "Natural E", 3.142 -> "Pi") //> m : scala.collection.immutable.TreeMap[Double,String] = Map(0.3333333 -> A //| third, 2.71828 -> Natural E, 3.142 -> Pi) m(3.142) //> res0: String = Pi (m: MapIndexerExtension)(2) //> res1: (Double, String) = (3.142,Pi) m(2 : Int) //> java.util.NoSuchElementException: key not found: 2.0 m(2) //> java.util.NoSuchElementException: key 查找,当然它失败了。即使我们将其归为TreeMap.apply,也会发生这种情况 res1 是可以的,因为我们在: Int上使用了类型归属。

在Theon的回答示例中,您看不到StringOps和String调用m之间的任何区别,但我认为这是因为它们意味着产生相同的输出,即使它们是实施方式不同。

为什么会这样,我不知道。如果你问我,这很令人困惑;但我认为这是为了阻止人们做我在上面例子中所展示的内容。