加载像
这样的ficus配置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
,它可以正常工作。我在这里缺少什么?
答案 0 :(得分:8)
Ficus使用type class pattern,它允许您通过指定必须为它们使用的操作来约束泛型类型。 Ficus还提供了类型类实例“派生”,在这种情况下,它由一个宏提供支持,该宏可以检查特定类案例类型的结构并自动创建一个类型类实例。
在这种情况下的问题是T
不是特定类的类类型 - 它是扩展Product
的任何旧类型,这可能是这样的好东西:
case class EasyToDecode(a: String, b: String, c: String)
但它也可能是:
trait X extends Product {
val youWillNeverDecodeMe: String
}
您从ArbitraryTypeReader
导入的宏目前不知道,因为此处T
是通用的。所以你需要一个不同的方法。
此处的相关类型类为ValueReader
,您可以将代码最小化更改为以下内容,以确保T
具有ValueReader
实例(请注意{{1}这里的语法就是所谓的“上下文绑定”):
T: ValueReader
这指定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]
或其{{1}的位置说明其他内容实例需要来自。
使用具体类型T
调用此方法的人有几个选项。对于许多标准库类型,Ficus提供了自动可用的实例,因此,如果ValueReader
是{ MyType
,他们都已经确定了:
MyType
如果Int
是自定义类型,那么他们可以手动定义自己的scala> ValueReader[Int]
res0: net.ceedubs.ficus.readers.ValueReader[Int] = net.ceedubs.ficus.readers.AnyValReaders$$anon$2@6fb00268
实例,也可以导入其他人定义的实例,或者他们可以使用泛型推导(这是什么MyType
确实如此。
这里的关键点是类型类模式允许您作为泛型方法的作者指定所需的操作,而不说明如何为具体类型定义这些操作。您只需编写ValueReader[MyType]
,并且您的来电者根据需要导入ArbitraryTypeReader
。