I have 2 variables declared as follows,
implicit val decodeURL: Decoder[URL] = Decoder.decodeString.emapTry(s => Try(new URL(s))) // #1
implicit val decodeCompleted = Decoder[List[URL]].prepare(_.downField("completed")) // #2
Run Code Online (Sandbox Code Playgroud)
Both lines compile and run.
However, if I annotate #2 with type i.e. implicit val decodeCompleted: Decoder[List[URL]] = Decoder[List[URL]].prepare(_.downField("completed"))
. It compiles and #2 will throw NullPointerException
(NPE) during runtime.
How could this happen? I don't know if this is Circe or just plain Scala issue. Why #2 is different from #1? Thanks
问题是您应该始终使用带有注释的隐式。
现在,当您在没有注释的情况下使用它们时,您会进入某种未定义/无效的行为区域。这就是为什么在未注释的情况下会发生这样的事情:
Decoder[List[URL]]
Decoder[List[URL]]
范围内没有(带注释的)隐含内容generic.auto._
因为它的定义在伴生对象中)Decoder[List[URL]]
,因此推断类型为decodeCompleted
现在,如果您注释会发生什么?
Decoder[List[URL]]
decodeCompleted
声明为满足该定义的东西decodeCompleted
使用价值decodeCompleted
没有初始化!事实上我们现在正在初始化它!decodeCompleted = null
这实际上等于:
val decodeCompleted = decodeCompleted
Run Code Online (Sandbox Code Playgroud)
只是间接层妨碍了编译器发现这种做法的荒谬性。(如果替换val
为,def
最终会导致无限递归和堆栈溢出):
@ implicit val s: String = implicitly[String]
s: String = null
@ implicit def s: String = implicitly[String]
defined function s
@ s
java.lang.StackOverflowError
ammonite.$sess.cmd1$.s(cmd1.sc:1)
ammonite.$sess.cmd1$.s(cmd1.sc:1)
ammonite.$sess.cmd1$.s(cmd1.sc:1)
ammonite.$sess.cmd1$.s(cmd1.sc:1)
Run Code Online (Sandbox Code Playgroud)
是的,它被编译器弄乱了。你没有做错任何事,在完美的世界里它会起作用。
Scala 社区通过区分以下内容来缓解这一问题:
在后一种情况下,您通常有一些实用程序,例如:
import io.circe.generic.semiauto._
implicit val decodeCompleted: Decoder[List[URL]] = deriveDecoder[List[URL]]
Run Code Online (Sandbox Code Playgroud)
它之所以有效,是因为它需要DerivedDecoder[A]
隐式,然后从中提取Decoder[A]
,所以你永远不会以场景结束implicit val a: A = implicitly[A]
。
归档时间: |
|
查看次数: |
83 次 |
最近记录: |