Scala在哪里寻找它?

Scala在哪里寻找它?,scala,implicit-conversion,implicits,Scala,Implicit Conversion,Implicits,对于Scala的新手来说,一个隐含的问题似乎是:编译器在哪里寻找隐式?我的意思是含蓄的,因为这个问题似乎从来没有完全形成过,好像没有词语来形容它一样。:-)例如,下面的integral值来自哪里 scala> import scala.math._ import scala.math._ scala> def foo[T](t: T)(implicit integral: Integral[T]) {println(integral)} foo: [T](t: T)(implici

对于Scala的新手来说,一个隐含的问题似乎是:编译器在哪里寻找隐式?我的意思是含蓄的,因为这个问题似乎从来没有完全形成过,好像没有词语来形容它一样。:-)例如,下面的
integral
值来自哪里

scala> import scala.math._
import scala.math._

scala> def foo[T](t: T)(implicit integral: Integral[T]) {println(integral)}
foo: [T](t: T)(implicit integral: scala.math.Integral[T])Unit

scala> foo(0)
scala.math.Numeric$IntIsIntegral$@3dbea611

scala> foo(0L)
scala.math.Numeric$LongIsIntegral$@48c610af
对于那些决定学习第一个问题答案的人来说,另一个问题是,在某些明显模棱两可的情况下,编译器如何选择使用哪个隐式

例如,
scala.Predef
定义了从
String
WrappedString
StringOps
的两种转换。然而,这两个类都有很多方法,所以Scala为什么不在调用
map
时抱怨不明确呢

注意:这个问题的灵感来源于,希望以更一般的方式说明这个问题。这个例子是从那里复制过来的,因为答案中提到了它。

隐式类型 Scala中的隐式指的是可以“自动”传递的值,或者是自动进行的从一种类型到另一种类型的转换

隐式转换 简单地说后一种类型,如果在类
C
的对象
o
上调用方法
m
,而该类不支持方法
m
,那么Scala将寻找从
C
到支持
m
的隐式转换。一个简单的例子是
String
上的方法
map

"abc".map(_.toInt)
String
不支持方法
map
,但
StringOps
支持,并且有一个从
String
StringOps
的隐式转换可用(请参见
Predef
上的
implicit def augmentString

隐式参数 另一种隐式参数是隐式参数。这些参数像任何其他参数一样传递给方法调用,但编译器会尝试自动填充它们。如果不能,它就会抱怨。您可以显式地传递这些参数,例如,这就是如何使用
breakOut
(请参阅关于
breakOut
的问题,在您感觉到挑战的一天)

在这种情况下,必须声明需要隐式声明,例如
foo
方法声明:

def foo[T](t: T)(implicit integral: Integral[T]) {println(integral)}
视图边界 有一种情况,隐式表达式既是隐式转换又是隐式参数。例如:

def getIndex[T, CC](seq: CC, value: T)(implicit conv: CC => Seq[T]) = seq.indexOf(value)

getIndex("abc", 'a')
for {
    x <- List(1, 2, 3)
    y <- Some('x')
} yield (x, y)
class A(val n: Int)
object A { 
    implicit def str(a: A) = "A: %d" format a.n
}
class B(val x: Int, y: Int) extends A(y)
val b = new B(5, 2)
val s: String = b  // s == "A: 2"
class A(val n: Int) {
  def +(other: A) = new A(n + other.n)
}
object A {
  implicit def fromInt(n: Int) = new A(n)
}

// This becomes possible:
1 + new A(1)
// because it is converted into this:
A.fromInt(1) + new A(1)
方法
getIndex
可以接收任何对象,只要它的类可以隐式转换为
Seq[T]
。因此,我可以将
字符串
传递给
getIndex
,这样它就可以工作了

在幕后,编译器将
seq.IndexOf(value)
更改为
conv(seq.IndexOf(value)

这是如此有用,以至于有语法糖来编写它们。使用这种语法糖,
getIndex
可以这样定义:

def getIndex[T, CC <% Seq[T]](seq: CC, value: T) = seq.indexOf(value)
def sum[T : Integral](list: List[T]): T = {
    val integral = implicitly[Integral[T]]
    import integral._   // get the implicits in question into scope
    list.foldLeft(integral.zero)(_ + _)
}
它还有一种语法上的糖分,称为上下文绑定,由于需要引用隐式语法,上下文绑定就变得不那么有用了。该方法的直接转换如下所示:

def getIndex[T, CC <% Seq[T]](seq: CC, value: T) = seq.indexOf(value)
def sum[T : Integral](list: List[T]): T = {
    val integral = implicitly[Integral[T]]
    import integral._   // get the implicits in question into scope
    list.foldLeft(integral.zero)(_ + _)
}
当您只需要将上下文边界传递给使用它们的其他方法时,上下文边界更有用。例如,
Seq
上的
sorted
方法需要隐式
排序。要创建方法
reverseSort
,可以编写:

def reverseSort[T : Ordering](seq: Seq[T]) = seq.sorted.reverse
因为
Ordering[T]
被隐式传递给
reverseSort
,所以它可以将其隐式传递给
排序的

它来自哪里? 当编译器发现需要隐式参数时,无论是因为调用对象类上不存在的方法,还是因为调用需要隐式参数的方法,它都会搜索符合需要的隐式参数

此搜索遵循某些规则,这些规则定义哪些隐式可见,哪些不可见。下表显示了编译器将在何处搜索隐式,它摘自Josh Suereth的一篇优秀的关于隐式的文章,我衷心地向希望提高Scala知识的人推荐这篇文章。自那时以来,它得到了反馈和更新的补充

下面第1项下可用的隐式优先于第2项下的隐式。除此之外,如果有多个符合条件的参数与隐式参数的类型匹配,则将使用静态重载解析规则选择最具体的参数(参见Scala规范§6.26.3)。更详细的信息可以在我在这个答案后面的一个问题中找到

  • 当前范围内的首次查看
    • 在当前范围中定义的隐式
    • 显式导入
    • 通配符导入
    • 其他文件中的相同范围
  • 现在看一下中的关联类型
    • 类型的伴生对象
    • 参数类型的隐式范围(2.9.1)
    • 类型参数的隐式范围(2.8.0)
    • 嵌套类型的外部对象
    • 其他维度
  • 让我们给他们举一些例子:

    在当前范围中定义的隐式 显式导入 通配符导入 其他文件中的相同范围 编辑:这似乎没有不同的优先级。如果你有一些例子说明了优先权的区别,请发表评论。否则,不要依赖这个

    这与第一个示例类似,但假设隐式定义位于与其用法不同的文件中。另请参见如何在中使用来引入隐式

    类型的伴生对象 这里有两个值得注意的对象。首先,研究“源”类型的对象伴侣。例如,在对象
    选项中有一个隐式转换
    
    List(1, 2, 3).flatMap(x => Some('x').map(y => (x, y)))
    
    List(1, 2, 3).sorted
    
    class A(val n: Int)
    object A { 
        implicit def str(a: A) = "A: %d" format a.n
    }
    class B(val x: Int, y: Int) extends A(y)
    val b = new B(5, 2)
    val s: String = b  // s == "A: 2"
    
    class A(val n: Int) {
      def +(other: A) = new A(n + other.n)
    }
    object A {
      implicit def fromInt(n: Int) = new A(n)
    }
    
    // This becomes possible:
    1 + new A(1)
    // because it is converted into this:
    A.fromInt(1) + new A(1)
    
    class A(val n: Int)
    object A {
        implicit val ord = new Ordering[A] {
            def compare(x: A, y: A) = implicitly[Ordering[Int]].compare(x.n, y.n)
        }
    }
    
    List(new A(5), new A(2)).sorted
    
    class A(val n: Int) {
      class B(val m: Int) { require(m < n) }
    }
    object A {
      implicit def bToString(b: A#B) = "B: %d" format b.m
    }
    val a = new A(5)
    val b = new a.B(3)
    val s: String = b  // s == "B: 3"