考虑到一个可接受的解决方案允许将预处理功能移动到相关对象,主要剩余困难在于您希望能够抽象出情况类构造函数的元数和类型(即形状)。这可以通过HList
实现和来自shapeless的多态函数值实现。
首先是一些准备工作,
import shapeless.HList._
import shapeless.Functions._
import shapeless.Poly._
import shapeless.TypeOperators._
case class Input[T](value: T)
object value extends (Input ~> Id) {
def default[T](i : Input[T]) = i.value
}
我们现在可以定义一个预处理器基类,它提供了一个apply方法,该方法接受一组
Input
类型的
HList
,将多态函数
value
映射到其中(即执行预处理),然后将生成的非
Input
类型的
HList
传递给提供的案例类构造函数(以hlisted形式给出,请参见下文)。
abstract class Preprocessor[In <: HList, Out <: HList, R](ctor : Out => R)
(implicit mapper : MapperAux[value.type, In, Out]) {
def apply(in : In) = ctor(in map value)
}
现在,我们定义一个带有后处理组件类型的案例类。
case class Foo(input1 : Int, input2 : String)
并且添加一行样板代码。
object FooBuilder extends Preprocessor((Foo.apply _).hlisted)
现在我们可以使用FooBuilder
构建Foo
实例了,其中Preprocessor
构造函数参数以HListed形式作为所需的补充对象工厂方法提供。
val foo = FooBuilder(Input(23) :: Input("foo") :: HNil)
很遗憾,目前无法将
FooBuilder
对象与
Foo
伴生对象组合使用:如果您尝试让
Foo
伴生对象扩展
Preprocessor
,您会发现
Foo
工厂方法不可用作
Preprocessor
构造函数参数。
为了说明该解决方案确实在类型和元数上进行了抽象,以下是如何添加第二个形状不同的case class的示例。
case class Bar(input1 : Int, input2 : String, input3 : Boolean)
object BarBuilder extends Preprocessor((Bar.apply _).hlisted)
val bar = BarBuilder(Input(23) :: Input("foo") :: Input(true) :: HNil)
run
的签名是否可以替换Foo
的构造函数签名? - Miles Sabin