我有一个使用Slick进行数据库访问的测试套件。此套件中的某些测试访问数据库而某些测试不访问。 我的套房有
implicit val db = DB.getDB
在套件执行开始时有效初始化DataBaseDef
。然后,此值将用作某些方法的隐式参数值。
它还有afterAll()
,它在套件执行结束时关闭db
:
override def afterAll():Unit={
db.close()
super.afterAll()
}
现在,如果我改为:
implicit lazy val db = DB.getDB
究竟会发生什么?
如果我只运行一个不使用DB的测试,那么连接将不会被初始化,并且afterAll()
它仍会尝试关闭连接,在这种情况下我有问题,对吧?我试图运行,但没有发生错误,没有抛出任何异常......
我对implicits的了解不足以帮助我理解它与懒惰的结合。
答案 0 :(得分:7)
那究竟会发生什么?
在第一次访问该值之前,该值不会被初始化。
它仍然会尝试关闭连接,我有一个问题 好吧,对吧?
当您访问db.close()
时,它会首先初始化该值,这意味着它会在关闭连接之前调用DB.getDb
。这意味着,虽然您并不打算这样做,但连接仍会初始化,然后立即关闭,因此您不会看到异常。
答案 1 :(得分:1)
要添加到已接受的答案,我想指出隐式在编译时解决,而val
lazy val
和def
仅在运行时有影响。
如果将标识符定义为隐式标识符,它将告诉编译器您可以访问给定类型的隐式值,因此任何需要这种隐式参数的方法都将使用它;相反,如果您没有声明给定类型的隐式标识符,则对需要该类型值的方法的任何调用都将引发编译器错误。但是,一旦代码被编译,就不再需要implicits了(它们已被相关标识符的显式引用所取代,具体取决于范围)。
现在,在运行时,当一个隐式标识符被调用时,它将使用标准规则进行实例化,因此它可以在定义时进行实例化和memoized,如果它是{{1如果它是val
,则在使用时实例化和记忆,或者如果它是lazy val
,则在每次使用时实例化。它曾经是一个隐式参数的事实对实例化规则没有影响。