内隐类型
可以说,Scala中的隐式是指可以“自动”传递的值,或者是指从一种类型到另一种类型的自动转换。
隐式转换
说到非常简单说一下后一种类型,如果调用一个方法m
的对象上o
的一类C
,而该类不是不支持的方法m
,然后将斯卡拉寻找从隐式转换C
的东西,不支持m
。一个简单的例子是方法map
上String
:
"abc".map(_.toInt)
String
不支持method map
,但是支持StringOps
,并且存在从String
到的隐式转换StringOps
(请参阅implicit def augmentString
参考资料Predef
)。
隐式参数
另一种隐式是隐式参数。它们像其他任何参数一样传递给方法调用,但是编译器会尝试自动填充它们。如果不能,它将抱怨。一个可以明确地传递这些参数,这是怎么一个用途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')
getIndex
只要方法的类可以进行隐式转换,该方法就可以接收任何对象Seq[T]
。因此,我可以将传递String
给getIndex
,它将起作用。
在后台,编译器更改seq.IndexOf(value)
为conv(seq).indexOf(value)
。
这是如此有用,以至于用语法糖来编写它们。使用这种语法糖,getIndex
可以这样定义:
def getIndex[T, CC <% Seq[T]](seq: CC, value: T) = seq.indexOf(value)
这种语法糖被描述为类似于上限()或下限()的视图边界。CC <: Seq[Int]
T >: Null
上下文界限
隐式参数中的另一个常见模式是类型类模式。这种模式可以为未声明它们的类提供通用接口。它既可以充当桥接模式(获得关注的分离),又可以充当适配器模式。
Integral
您提到的类是类型类模式的经典示例。Scala标准库的另一个示例是Ordering
。有一个库大量使用了这种模式,称为Scalaz。
这是其用法的一个示例:
def sum[T](list: List[T])(implicit integral: Integral[T]): T = {
import integral._ // get the implicits in question into scope
list.foldLeft(integral.zero)(_ + _)
}
它也有一种语法糖,称为上下文绑定,由于需要引用隐式而变得不太有用。该方法的直接转换如下所示:
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)(_ + _)
}
当您仅需要将上下文边界传递给使用它们的其他方法时,上下文边界会更有用。例如,sorted
on上的方法Seq
需要隐式Ordering
。要创建一个方法reverseSort
,可以编写:
def reverseSort[T : Ordering](seq: Seq[T]) = seq.sorted.reverse
因为Ordering[T]
已隐式传递给reverseSort
,所以可以将其隐式传递给sorted
。
内隐来自哪里?
当编译器认为需要隐式时,要么是因为您正在调用对象类上不存在的方法,要么是因为您正在调用需要隐式参数的方法,则编译器将搜索适合需要的隐式。
该搜索遵循某些规则,这些规则定义了哪些隐式可见,哪些不可见。下表显示编译器将用于implicits搜索是从一个出色的拍摄表现有关乔希Suereth,implicits我衷心地推荐给任何人想要提高自己的知识斯卡拉。从那时起,它就得到了反馈和更新的补充。
下面数字1下可用的隐式优先于数字2下的隐式。除此之外,如果有多个与隐式参数类型匹配的合格参数,则将使用静态重载解析规则选择一个最具体的参数(请参阅Scala)。规范§6.26.3)。我可以在此答案末尾链接的问题中找到更多详细信息。
- 首先看一下当前范围
- 当前范围中定义的隐式
- 明确进口
- 通配符导入
其他文件中的作用域相同
- 现在来看一下
- 类型的同伴对象
- 参数类型的隐式范围(2.9.1)
- 类型参数的隐式范围(2.8.0)
- 嵌套类型的外部对象
- 其他尺寸
让我们为他们举一些例子:
当前范围中定义的隐式
implicit val n: Int = 5
def add(x: Int)(implicit y: Int) = x + y
add(5) // takes n from the current scope
明确进口
import scala.collection.JavaConversions.mapAsScalaMap
def env = System.getenv() // Java map
val term = env("TERM") // implicit conversion from Java Map to Scala Map
通配符导入
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)(_ + _)
}
其他文件中的作用域相同
编辑:似乎这没有不同的优先级。如果您有一些示例可以说明优先级区别,请发表评论。否则,请不要依赖于此。
这与第一个示例类似,但是假定隐式定义与使用的文件位于不同的文件中。另请参阅如何使用包对象来引入隐式。
类型的同伴对象
这里有两个值得注意的对象伴侣。首先,研究“源”类型的对象伴侣。例如,在对象内部进行Option
了隐式转换Iterable
,因此可以Iterable
在上调用方法Option
,或传递Option
给需要的东西Iterable
。例如:
for {
x <- List(1, 2, 3)
y <- Some('x')
} yield (x, y)
该表达式由编译器翻译为
List(1, 2, 3).flatMap(x => Some('x').map(y => (x, y)))
然而,List.flatMap
预计一TraversableOnce
,这Option
是没有的。然后,编译器在内部Option
的对象伴侣中查找并找到对的转换Iterable
,该转换为TraversableOnce
,使该表达式正确。
第二,预期类型的伴随对象:
List(1, 2, 3).sorted
该方法sorted
采用隐式Ordering
。在这种情况下,它将在对象Ordering
,类的伴侣中Ordering
查找,并在Ordering[Int]
那里找到一个隐式对象。
请注意,还将研究超类的伴随对象。例如:
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"
这是斯卡拉如何发现隐含的Numeric[Int]
,并Numeric[Long]
在你的问题,顺便说一下,因为他们发现里面Numeric
不是Integral
。
参数类型的隐式范围
如果您有一个带有参数类型的方法A
,那么A
还将考虑类型的隐式范围。“隐式范围”是指所有这些规则都将递归应用-例如,按照上述规则,将在的伴随对象中A
搜索隐式。
请注意,这并不意味着A
将搜索隐式范围的参数转换,而是搜索整个表达式。例如:
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)
从Scala 2.9.1开始可用。
类型参数的隐式范围
这是使类型类模式真正起作用所必需的。例如Ordering
,考虑一下:它在其伴随对象中带有一些隐式对象,但是您不能向其中添加内容。那么如何Ordering
为自己的班级自动创建一个呢?
让我们从实现开始:
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
如我们所见,该方法sorted
期望一个Ordering[A]
(实际上,它期望一个Ordering[B]
,其中B >: A
)。内部没有任何此类内容Ordering
,也没有可查看的“源”类型。显然,这是里面找到它A
,这是一个类型参数的Ordering
。
这也是各种期望的收集方法的CanBuildFrom
工作方式:隐式可以在的对象类型内找到的类型参数CanBuildFrom
。
注意:Ordering
定义为trait Ordering[T]
,其中T
是类型参数。之前,我说过Scala会查看类型参数的内部,这没有什么意义。看了上述隐式是Ordering[A]
,在这里A
是一个实际的类型,而不是输入参数:它是一个类型参数来Ordering
。参见Scala规范的7.2节。
从Scala 2.8.0开始可用。
嵌套类型的外部对象
我实际上还没有看到这样的例子。如果有人可以分享我,我将不胜感激。原理很简单:
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"
其他尺寸
我敢肯定这是个玩笑,但是这个答案可能不是最新的。因此,请勿将此问题视为正在发生的事情的最终仲裁者,如果您确实注意到它已经过时,请通知我以便我可以解决。
编辑
感兴趣的相关问题: