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。