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”的缩写吗?我有点困惑。谢谢!
答案 0 :(得分:6)
通常情况下,当您调用对于给定类型不存在的方法时,Scala编译器将执行隐式转换:
case class Foo(x :String)
implicit class Bar(foo: Foo) {
def barOnly = "w00p"
}
println( Foo("test").barOnly )
当我们在barOnly
的实例上调用方法Foo
时,scala编译器可以看到它需要执行从Foo
到Bar
的隐式转换以便为我们提供使用该方法,我们得到w00p
的预期输出。 Try it
但是如果Foo
和Bar
中存在具有相同签名的方法,那么我们会有一些阴影,并且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
在scaladocs中split
的示例中,split
和String
上都有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
可能只是文档中的错误。
答案 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
之间的任何区别,但我认为这是因为它们意味着产生相同的输出,即使它们是实施方式不同。
为什么会这样,我不知道。如果你问我,这很令人困惑;但我认为这是为了阻止人们做我在上面例子中所展示的内容。