6

StringOps文档中有一个Shadowed Implicit Value Members部分。例如:

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

4 回答 4

6

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

case class Foo(x :String)

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

println( Foo("test").barOnly )

barOnly在这里,当我们在 scala 编译器的实例上调用该方法时,可以看到它需要从toFoo进行隐式转换以向我们提供该方法,并且我们得到了预期的输出。试试看FooBarw00p

Foo但是,如果在and中存在具有相同签名的方法Bar,那么我们会有一些阴影,并且 scala 编译器不会进行隐式转换,除非我们使用类型归属显式请求它:

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

试试看

在您split的 scaladocs 示例中,两者都调用split了方法StringStringOps,但是它们采用不同的参数类型,所以我不完全确定为什么文档警告我们必须使用类型归属。在这种情况下,我们不需要为编译器消除任何歧义,类型归属没有任何效果:

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

试试看

也许只是文档中的一个错误。

于 2013-10-27T19:37:44.490 回答
0

"aaa bbb ccc" is a String object, not a StringOps object, so the comment is not relevant.

This is a corner case where you would probably never create a StringOps object explicitly, only ever relying on implicit conversions, so this Scaladoc comment seems kind of useless.

于 2013-10-27T08:57:13.423 回答
0

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

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

这个 Stack Overflow 问题中解释了类型归属

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> 
于 2013-10-27T10:39:13.380 回答
-1

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

以 的apply方法TreeMap为例。假设我想用一个采用零索引整数位置的方法来重载它,所以我可以TreeMap像数组一样索引:

 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 

你会注意到最后几行失败了。这里发生的事情是 2 被转换为双精度,然后用 查找TreeMap.apply,当然它失败了。即使我们使用: Int res1来命名它也会发生这种情况,因为我们在m.

在 Theon 回答的示例中,您看不到 StringOps 和 String call to 之间的任何区别split,但我认为这是因为它们旨在产生相同的输出,即使它们的实现方式不同。

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

于 2014-03-10T15:59:07.997 回答