【问题标题】:ficus configuration load generic榕树配置加载通用
【发布时间】:2017-12-09 19:37:37
【问题描述】:

加载榕树配置,如

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

当直接指定一个案例类而不是TSomeClass 时,它工作得很好。我在这里错过了什么?

【问题讨论】:

    标签: scala class-hierarchy classtag


    【解决方案1】:

    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 语法是所谓的“上下文绑定”):

    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

    【讨论】:

    • 所以我需要创建一个隐式值读取器吗?目前,我得到:找不到 net.ceedubs.ficus.readers.ValueReader 类型的证据参数的隐含值 - 即使添加 def loadConfiguration[T: ValueReader](implicit reader: ArbitraryTypeReader): T = { 也不能解决它。同样为调用者导入import net.ceedubs.ficus.readers.ArbitraryTypeReader._ 也无法解决问题。
    • 不,你绝对不需要(或想要)隐含的ArbitraryTypeReader,只需要ValueReader。鉴于上述定义,您只需导入ArbitraryTypeReader._(在使用站点,而不是您定义loadConfiguration 的位置)并调用loadConfiguration[EasyToDecode]
    • 我尝试按照您的指示进行操作 - 但它似乎还不起作用。 ArbitraryTypeReader._ 在调用方导入,即loadConfiguration[SomeCaseClass],该方法看起来就像您建议的那样,但是:Cannot generate a config ... 的错误仍然存​​在。尝试implicit val valueReader = ValueReader[HashingConfiguration] 失败。
    • @GeorgHeiler “不起作用”是什么意思?一个简单的案例类(如答案中的那个)可以编译吗?
    • 那很奇怪——loadConfiguration[EasyToDecode] 对我来说编译得很好(这是我所期望的)。您要同时导入 Ficus._ArbitraryTypeReader._
    猜你喜欢
    • 2011-06-20
    • 1970-01-01
    • 2019-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-08
    • 2016-04-21
    相关资源
    最近更新 更多