榕树配置加载通用
ficus configuration load generic
正在加载像
这样的榕树配置
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
当直接指定大小写 class 而不是 T
时,即 SomeClass
它工作得很好。我在这里错过了什么?
Ficus 使用 type class pattern,它允许您通过指定必须对它们可用的操作来约束泛型类型。 Ficus 还提供类型 class 实例 "derivation",在这种情况下,它由一个宏提供支持,该宏可以检查特定案例的结构 class-like 类型并自动创建类型 class实例.
这种情况下的问题是 T
不是特定情况下的 class-like 类型——它是任何扩展 Product
的旧类型,这可能是不错的东西这个:
case class EasyToDecode(a: String, b: String, c: String)
但也可以是:
trait X extends Product {
val youWillNeverDecodeMe: String
}
您从 ArbitraryTypeReader
导入的宏此时不知道,因为 T
在这里是通用的。所以你需要一个不同的方法。
此处的相关类型 class 是 ValueReader
,您可以将代码更改为类似以下内容以确保 T
具有 ValueReader
实例(请注意,这里的 T: ValueReader
语法就是所谓的 "context bound"):
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@6fb00268
如果 MyType
是自定义类型,那么他们可以手动定义自己的 ValueReader[MyType]
实例,或者可以导入其他人定义的实例,或者可以使用泛型派生(就是 ArbitraryTypeReader
所做的)。
这里的关键点是类型 class 模式允许您作为泛型方法的作者指定您需要的操作,而无需说明如何为具体类型定义这些操作。您只需编写 T: ValueReader
,您的调用者会根据需要导入 ArbitraryTypeReader
。
正在加载像
这样的榕树配置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
当直接指定大小写 class 而不是 T
时,即 SomeClass
它工作得很好。我在这里错过了什么?
Ficus 使用 type class pattern,它允许您通过指定必须对它们可用的操作来约束泛型类型。 Ficus 还提供类型 class 实例 "derivation",在这种情况下,它由一个宏提供支持,该宏可以检查特定案例的结构 class-like 类型并自动创建类型 class实例.
这种情况下的问题是 T
不是特定情况下的 class-like 类型——它是任何扩展 Product
的旧类型,这可能是不错的东西这个:
case class EasyToDecode(a: String, b: String, c: String)
但也可以是:
trait X extends Product {
val youWillNeverDecodeMe: String
}
您从 ArbitraryTypeReader
导入的宏此时不知道,因为 T
在这里是通用的。所以你需要一个不同的方法。
此处的相关类型 class 是 ValueReader
,您可以将代码更改为类似以下内容以确保 T
具有 ValueReader
实例(请注意,这里的 T: ValueReader
语法就是所谓的 "context bound"):
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@6fb00268
如果 MyType
是自定义类型,那么他们可以手动定义自己的 ValueReader[MyType]
实例,或者可以导入其他人定义的实例,或者可以使用泛型派生(就是 ArbitraryTypeReader
所做的)。
这里的关键点是类型 class 模式允许您作为泛型方法的作者指定您需要的操作,而无需说明如何为具体类型定义这些操作。您只需编写 T: ValueReader
,您的调用者会根据需要导入 ArbitraryTypeReader
。