一、概述
DeclaredImplicitly
是 Scala 中关于隐式转换的一个重要概念。它表示编译器会在代码上下文中,自动查找类型的隐式实例,并且自动将其转换成对应类型的值。在相应的上下文中,声明隐式变量或隐式函数将使得编译器能够根据上下文自动推断隐式转换所需要的类型。隐式转换使得类型转换更加灵活,同时也降低了代码的冗余度。
二、隐式实例查找
编译器在声明上下文中查找隐式实例,其查找顺序如下:
- 先查找当前作用域下的隐式对象。比如,当在某一个函数中使用隐式参数时,编译器会首先在当前函数的作用域中查找隐式实例。
- 如果当前作用域中没有找到符合条件的隐式实例,则编译器会搜索相关类型的伴生对象中的隐式实例,包括父类和包对象。
- 如果在伴生对象中也没有找到符合条件的隐式实例,则编译器会搜索隐式值的类型的父类和委托实现。
- 如果编译器找到了多个符合条件的隐式实例,则会编译错误。 下面以代码示例来说明隐式实例查找的过程:
trait A
case class B(i: Int) extends A
object C {
implicit val b: B = B(1)
}
def test(implicit a: A): Unit = println(a)
import C.b
test // 输出 B(1)
上述代码中,当执行 test
函数时,编译器会查找类型为 A
的隐式实例。由于当前作用域中找不到,则编译器会在伴生对象中查找,找到了类型为 B
的隐式实例,然后经过不断向上查找,最终找到了类型为 A
的隐式实例。
三、隐式参数
Scala 中的隐式参数是在函数参数列表中以 implicit
关键字声明的参数。在函数调用时,如果没有指定这些参数的值,则编译器会在当前作用域中查找符合条件的隐式实例。隐式参数可以使得函数调用更加简洁,并且使得类型转换更加简单方便。
下面以代码示例来说明隐式参数的作用:
trait Formatter[T] {
def format(value: T): String
}
class Person(val name: String, val age: Int)
object Formatters {
implicit val personFormatter: Formatter[Person] = new Formatter[Person] {
def format(person: Person) = s"${person.name} is ${person.age} years old"
}
}
def printFormatted[T](value: T)(implicit formatter: Formatter[T]): Unit =
println(formatter.format(value))
import Formatters.personFormatter
val person = new Person("Alice", 25)
printFormatted(person) // 输出 Alice is 25 years old
上述代码中,定义了一个 Formatter
类型的隐式实例,用于格式化 Person
类型的数据。在函数 printFormatted
的参数列表中,定义了一个隐式参数 formatter
,编译器会在当前作用域中查找符合条件的隐式实例。由于在上下文中已经定义了符合条件的隐式实例 personFormatter
,因此编译器会将该实例自动传入函数中。
四、隐式函数
Scala 中的隐式函数是定义在对象或类中的函数,以 implicit
关键字声明。在编译器在调用函数时需要进行类型转换时,会自动查找符合条件的隐式函数进行类型转换。隐式函数可以使得代码更加简洁、优雅。
下面以代码示例来说明隐式函数的作用:
case class Complex(re: Double, im: Double)
class RichComplex(c: Complex) {
def +(that: Complex): Complex =
Complex(this.re + that.re, this.im + that.im)
}
implicit def complex2RichComplex(c: Complex): RichComplex = new RichComplex(c)
val c1 = Complex(1.0, 2.0)
val c2 = Complex(3.0, 4.0)
val c3 = c1 + c2 // 实际上等价于:c1.+(c2),输出 Complex(4.0,6.0)
上述代码中,定义了一个 Complex
类型的隐式转换函数 complex2RichComplex
,用于将 Complex
类型转换成 RichComplex
类型。当在 c1
实例上调用 +
方法时,由于 +
方法的参数列表中声明了 Complex
类型的参数,编译器会在当前作用域中查找符合条件的隐式函数进行类型转换。由于当前作用域中有符合条件的隐式函数 complex2RichComplex
,因此编译器将该函数自动应用到 c1
实例上,将其类型转换成 RichComplex
类型。
五、隐式类
Scala 2.10 之后,引入了隐式类的概念,它可以在类的外部隐式地定义一个类,用于包装某个类型的实例。当程序在正确的上下文中需要对该实例进行操作时,隐式类可以进行隐式转换,使其具备自定义的功能。 下面以代码示例来说明隐式类的作用:
implicit class IntOps(x: Int) {
def times[T](f: => T): Unit = {
def loop(current: Int): Unit =
if(current > 0) {
f
loop(current - 1)
}
loop(x)
}
}
5.times(println("Hello World!")) // 输出 5 次 Hello World!
上述代码中,声明了一个隐式类 IntOps
,定义了一个 times
方法,用于执行若干次某个操作。在程序中,使用了类 IntOps
对基本类型 Int
进行隐式转换,使其在正确的上下文中具备执行多次某个操作的功能。
六、总结
隐式转换是 Scala 中的一个重要特性,用于简化代码并提高代码的可读性。本文从隐式实例查找、隐式参数、隐式函数、隐式类等方面对该特性进行了详细的介绍和讲解。在代码中,我们可以通过定义隐式实例来让编译器在需要时自动进行类型转换,以达到更加优雅、简洁的代码效果,同时也提高了代码的复用性和可维护性。