传播Scala类型参数

时间:2016-04-15 18:35:56

标签: scala

这是我认为人们通常想在Scala中做的事情,但如果我能在任何地方找到一个例子,那我就该死了。

由于类型擦除,此代码无法编译,但它演示了我正在尝试完成的任务:

def parse[T](json: JsValue): T = {
  json.validate[T] match {
    case JsSuccess(x, _) => x
    case JsError(errors) => throw new MyException(errors.toString)
  }
}

我看到从擦除中保留类型信息的方式是添加隐式ClassTag参数:

def parse[T](json: JsValue)(implicit ct: ClassTag[T]): T = {
  json.validate[T] match {
    case JsSuccess(x, _) => x
    case JsError(errors) => throw new MyException(errors.toString)
  }
}

但......现在怎么样?如果validate方法采用ClassTag参数,我想我可以直接传递它:

json.validate(ct)

...但它没有,它需要一个隐含的Reads[T],我无法辨别如何从ClassTag[T]Reads[T]

是否可以像这样任意传播类型信息?如果是这样,怎么样?或者我的期望太高,这是不可能的,这就是为什么我找不到任何例子?

2 个答案:

答案 0 :(得分:5)

这不是类型擦除问题。您可以将类型参数T传递给validate,但它尚未被删除。问题是您需要在范围内提供隐式Reads[T]。您可以通过将其添加为上下文绑定来轻松解决此问题:

def parse[T: Reads](json: JsValue): T = {
  json.validate[T] match {
    case JsSuccess(x, _) => x
    case JsError(errors) => throw new MyException(errors.toString)
  }
}

这消除了:

def parse[T](json: JsValue)(implicit r: Reads[T]): T = {
  json.validate[T] match {
    case JsSuccess(x, _) => x
    case JsError(errors) => throw new MyException(errors.toString)
  }
}

请注意,此方法或多或少等同于编写json.as[T],尽管有不同的例外。

我认为混淆的根源可能是错误:

<console>:16: error: type mismatch;
 found   : x.type (with underlying type Any)
 required: T
           case JsSuccess(x, _) => x
                                   ^

但第一个错误实际上解释了这个问题:

<console>:15: error: No Json deserializer found for type T. Try to implement an implicit Reads or Format for this type.
         json.validate[T] match {
                      ^

第二个错误是由第一个错误引起的(编译器可能已经停在那里),因此第二个错误在这种情况下没有帮助。

答案 1 :(得分:1)

您应该将隐式Reads传递给解析方法:

def parse[T:Reads](json: JsValue): T = {
  json.validate[T] match {
    case JsSuccess(x, _) => x
    case JsError(errors) => throw new MyException(errors.toString)
  }
}
  

由于类型擦除

,此代码无法编译

不,如果你尝试编译它,你会得到类似的东西:&#34;找不到隐含的Reads [T] for parse&#34;