“什么是”呢;“隐藏的隐式值成员”;在Scala doc中?

“什么是”呢;“隐藏的隐式值成员”;在Scala doc中?,scala,Scala,文档中有一个隐藏的隐式值成员部分。例如: def拆分(arg0:String,arg1:Int):数组[String] 隐含信息 此成员是通过scala.Predef中的unauthengmentString方法执行的从StringOps到String的隐式转换添加的 阴影 该隐式继承的成员被该类中的一个或多个成员隐藏。 要访问此成员,可以使用类型归属: (stringOps: String).split(arg0, arg1) case class Foo(x :String) { de

文档中有一个隐藏的隐式值成员部分。例如:

def拆分(arg0:String,arg1:Int):数组[String]

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

阴影
该隐式继承的成员被该类中的一个或多个成员隐藏。 要访问此成员,可以使用类型归属:

(stringOps: String).split(arg0, arg1)
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 )
定义类

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


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

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

那么,隐藏的隐式值成员指的是什么?我试着问谷歌,但找不到任何参考资料

是不是有点像:

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
函数签名不同,因此不需要“类型归属”

这就是“隐藏的隐式值成员”的含义吗?我有点困惑。谢谢

“aaa bbb ccc”是字符串对象,而不是StringOps对象,因此注释不相关


在这种情况下,您可能永远不会显式创建StringOps对象,只会依赖隐式转换,因此Scaladoc注释似乎没有什么用处。

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

我们很少使用原始StringOps对象,因此实际上不太可能进行隐式转换

解释了类型归属


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

case class Foo(x :String)

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

println( Foo("test").barOnly )
这里,当我们在
Foo
实例上调用方法
barOnly
时,scala编译器可以看到它需要执行从
Foo
Bar
的隐式转换,以向我们提供该方法,并获得
w00p
的预期输出

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

(stringOps: String).split(arg0, arg1)
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
示例中,在
String
StringOps
上都有名为
split
的方法,但是它们采用不同的参数类型,所以我不完全清楚为什么文档会警告我们必须使用类型归属。在这种情况下,我们不需要为编译器消除任何歧义,类型归属没有任何效果:

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


可能只是文档中的一个错误。

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

TreeMap
apply
方法为例。假设我想用一个取零索引整数位置的方法重载它,这样我就可以索引到
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被转换成一个double,然后用
TreeMap.apply查找,当然失败了。即使我们将其归因于
:Int
不过res1还可以,因为我们在
m
上使用类型归属

在Theon的回答中的示例中,您看不出StringOps和对
拆分的字符串调用之间有任何区别,但我认为这是因为它们旨在产生相同的输出,即使它们的实现方式不同


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

“……但我认为这是因为它们应该产生相同的输出,即使它们的实现不同。”这是因为调用了类
java.lang.String
中定义的相同方法<代码>字符串操作
没有
拆分(arg0:String)