我有一个参数化的案例类CaseClass[T](name: String, t: T)
,我希望使用play-json(2.5)进行序列化/反序列化。
当然,如果我没有T
类型的等价物,我就不能拥有这个,所以我定义了
object CaseClass {
implicit def reads[T: Reads] = Json.reads[CaseClass[T]]
}
但是我得到以下编译器错误:
overloaded method value apply with alternatives:
[B](f: B => (String, T))(implicit fu: play.api.libs.functional.ContravariantFunctor[play.api.libs.json.Reads])play.api.libs.json.Reads[B] <and>
[B](f: (String, T) => B)(implicit fu: play.api.libs.functional.Functor[play.api.libs.json.Reads])play.api.libs.json.Reads[B]
cannot be applied to ((String, Nothing) => CaseClass[Nothing])
如果我尝试对Json.writes
宏做同样的事情,我会收到错误
type mismatch;
found : CaseClass[Nothing] => (String, Nothing)
required: CaseClass[T] => (String, T)
最令人惊讶的是,当我使用Json.format
宏时,都不会发生错误。
我知道我有不同的解决方案绕过这个问题(使用Json.format
,手工编写我的(de)序列化程序,...),但我很好奇为什么会发生这种情况这里。
答案 0 :(得分:1)
它是Json.reads
宏的限制,类型推断,或两者兼而有之。类型推断至少与它有一点关系,因为您可以看到错误消息中的某些内容被推断为Nothing
。
如果使用编译器标志-Ymacro-debug-lite
,则可以看到宏生成的AST。
implicit def reads[T](implicit r: Reads[T]): Reads[CaseClass[T]] =
Json.reads[CaseClass[T]]
转换为:
_root_.play.api.libs.json.JsPath.$bslash("name").read(json.this.Reads.StringReads)
.and(_root_.play.api.libs.json.JsPath.$bslash("t").read(r))
.apply((CaseClass.apply: (() => <empty>)))
清理,看起来像:
implicit def reads[T](implicit w: Reads[T]): Reads[CaseClass[T]] = (
(JsPath \ "name").read(Reads.StringReads) and
(JsPath \ "t" ).read(r)
)(CaseClass.apply _)
不幸的是,它没有编译,因为CaseClass.apply
的类型参数未提供,并被推断为Nothing
。手动将T
添加到apply
会修复此问题,但宏可能不知道T
中的CaseClass[T]
是重要的。
要更详细地解决类型推断问题,使用Reads
组合器,我们会调用FunctionalBuilder.CanBuild2#apply
,它需要(A1, A2) => B
。但编译器无法正确推断A2
。
对于Writes
,存在类似问题,我们需要B => (A1, A2)
,但编译器无法正确推断B
或A2
({{1}分别是}和CaseClass[T]
。
T
需要上述两个函数,并且编译器能够推断Format
必须为A2
。