加载一个榕树配置,如

loadConfiguration[T <: Product](): T = {
import net.ceedubs.ficus.readers.ArbitraryTypeReader._
import net.ceedubs.ficus.Ficus._
val config: Config = ConfigFactory.load()
config.as[T]

失败:
Cannot generate a config value reader for type T, because it has no apply method in a companion object that returns type T, and it doesn't have a primary constructor

当直接指定案例类而不是 T 时,即 SomeClass 它工作得很好。我在这里缺少什么?

最佳答案

Ficus 使用 type class pattern ,它允许您通过指定必须对它们可用的操作来约束泛型类型。 Ficus 还提供了类型类实例“派生”,在这种情况下,它由一个宏提供支持,该宏可以检查特定案例类类型的结构并自动创建类型类实例。

在这种情况下的问题是 T 不是一个特定的 case class-like 类型——它是任何扩展 Product 的旧类型,它可能是这样的:

case class EasyToDecode(a: String, b: String, c: String)

但也可能是:
trait X extends Product {
  val youWillNeverDecodeMe: String
}

您从 ArbitraryTypeReader 导入的宏此时不知道,因为 T 在这里是通用的。所以你需要一种不同的方法。

此处的相关类型类是 ValueReader ,您可以将代码至少更改为以下内容,以确保 T 具有 ValueReader 实例(请注意,此处的 T: ValueReader 语法是所谓的“上下文绑定(bind)”):
import net.ceedubs.ficus.Ficus._
import net.ceedubs.ficus.readers.ValueReader
import com.typesafe.config.{ Config, ConfigFactory }

def loadConfiguration[T: ValueReader]: T = {
  val config: Config = ConfigFactory.load()
  config.as[T]

}

这指定 T 必须有一个 ValueReader 实例(它允许我们使用 .as[T] )但没有说明 T 或其 ValueReader 实例需要来自哪里。

使用具体类型 MyType 调用此方法的人有几个选项。 Ficus 为许多标准库类型提供了随处自动可用的实例,因此如果 MyType 是例如Int ,它们都设置好了:
scala> ValueReader[Int]
res0: net.ceedubs.ficus.readers.ValueReader[Int] = net.ceedubs.ficus.readers.AnyValReaders$$anon$2@6fb00268

如果 MyType 是自定义类型,那么他们可以手动定义自己的 ValueReader[MyType] 实例,或者可以导入其他人定义的实例,或者可以使用泛型派生(这就是 ArbitraryTypeReader 所做的)。

这里的关键点是类型类模式允许您作为泛型方法的作者来指定您需要的操作,而无需说明如何为具体类型定义这些操作。您只需编写 T: ValueReader ,您的调用者就会根据需要导入 ArbitraryTypeReader

关于scala - 榕树配置加载通用,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/47732404/

10-10 04:46