无法在单例对象中找到隐式值
Could not find implicit value inside singleton object
我有这个代码:
trait Context {
implicit val e: Encoder
trait Encoder {
def write(): Unit = {
println("Test")
}
}
}
trait AsyncEncoders {
this: Context =>
class AsyncEncoder extends Encoder {
}
implicit val e = new AsyncEncoder()
}
class ConcreteContext extends Context with AsyncEncoders {
}
当我这样使用它时(案例1):
object Main extends App {
implicit val c = new ConcreteContext()
import c._
implicitly[Encoder].write()
}
然后编译并打印 Test
.
但是当我尝试在单例对象中调用相同的代码时(案例 2):
object TestObject {
def apply()(implicit c: ConcreteContext): Unit = {
import c._
implicitly[Encoder].write()
}
}
object Main extends App {
implicit val c = new ConcreteContext()
TestObject()
}
编译失败:
path/to/Main.scala:29: could not find implicit value for parameter e: c.Encoder
implicitly[c.Encoder].write()
如果我改变(案例 3):
implicit val e = new AsyncEncoder()
到
implicit val e: Encoder = new AsyncEncoder()
然后它会按预期编译和运行。
但出于某种原因,这对我来说是不可接受的。
为什么上面的情况会编译失败?
我认为问题不在于您使用对象这一事实,而在于您接受 ConcreteContext
作为参数:ConcreteContext.e
的类型是 AsyncEncoder
,不是 Encoder
.
很多次我观察到,当涉及到 Scala 时,除非另有说明,否则最好将参数视为不变量(例如,如果您不将 impl 类型转换为接口类型,macwire 经常会失败 - 它不完全虽然可以预测,但大部分时间它都有效)。
如您所见,将 e 类型显式设置为 Encoder
可以解决问题。将 ConcreteContext
更改为 Context
也是如此。我的猜测是,这要么是不变性问题,要么是编译器类型推理引擎的限制。
正如评论中所说,Scala 2.12.0 中没有问题。
对于 Scala 2.11.8,我使用了以下解决方法,假设 Encoder
只有一种方法:
trait Context {
implicit val e: Encoder
type BaseEncoder = () => Unit
type Encoder <: BaseEncoder
}
trait AsyncEncoders {
this: Context =>
type Encoder = AsyncEncoder
class AsyncEncoder extends BaseEncoder {
override def apply(): Unit = {
println("Test")
}
}
implicit val e = new AsyncEncoder()
}
class ConcreteContext extends Context with AsyncEncoders {
}
object TestObject {
def apply()(implicit c: ConcreteContext): Unit = {
import c._
implicitly[Encoder].apply()
}
}
object Main extends App {
implicit val c = new ConcreteContext()
TestObject()
}
我有这个代码:
trait Context {
implicit val e: Encoder
trait Encoder {
def write(): Unit = {
println("Test")
}
}
}
trait AsyncEncoders {
this: Context =>
class AsyncEncoder extends Encoder {
}
implicit val e = new AsyncEncoder()
}
class ConcreteContext extends Context with AsyncEncoders {
}
当我这样使用它时(案例1):
object Main extends App {
implicit val c = new ConcreteContext()
import c._
implicitly[Encoder].write()
}
然后编译并打印 Test
.
但是当我尝试在单例对象中调用相同的代码时(案例 2):
object TestObject {
def apply()(implicit c: ConcreteContext): Unit = {
import c._
implicitly[Encoder].write()
}
}
object Main extends App {
implicit val c = new ConcreteContext()
TestObject()
}
编译失败:
path/to/Main.scala:29: could not find implicit value for parameter e: c.Encoder implicitly[c.Encoder].write()
如果我改变(案例 3):
implicit val e = new AsyncEncoder()
到
implicit val e: Encoder = new AsyncEncoder()
然后它会按预期编译和运行。
但出于某种原因,这对我来说是不可接受的。
为什么上面的情况会编译失败?
我认为问题不在于您使用对象这一事实,而在于您接受 ConcreteContext
作为参数:ConcreteContext.e
的类型是 AsyncEncoder
,不是 Encoder
.
很多次我观察到,当涉及到 Scala 时,除非另有说明,否则最好将参数视为不变量(例如,如果您不将 impl 类型转换为接口类型,macwire 经常会失败 - 它不完全虽然可以预测,但大部分时间它都有效)。
如您所见,将 e 类型显式设置为 Encoder
可以解决问题。将 ConcreteContext
更改为 Context
也是如此。我的猜测是,这要么是不变性问题,要么是编译器类型推理引擎的限制。
正如评论中所说,Scala 2.12.0 中没有问题。
对于 Scala 2.11.8,我使用了以下解决方法,假设 Encoder
只有一种方法:
trait Context {
implicit val e: Encoder
type BaseEncoder = () => Unit
type Encoder <: BaseEncoder
}
trait AsyncEncoders {
this: Context =>
type Encoder = AsyncEncoder
class AsyncEncoder extends BaseEncoder {
override def apply(): Unit = {
println("Test")
}
}
implicit val e = new AsyncEncoder()
}
class ConcreteContext extends Context with AsyncEncoders {
}
object TestObject {
def apply()(implicit c: ConcreteContext): Unit = {
import c._
implicitly[Encoder].apply()
}
}
object Main extends App {
implicit val c = new ConcreteContext()
TestObject()
}