推断类型参数

Inferring type parameters

我正在与一些 java api 集成,看起来有点像这样(这些是 java 类,为了简洁起见,我将只使用 scala 语法:

 class AbstractFooBuilder[ActualType <: AbstractFooBuilder, WhatToBuild <: Foo] {
     // ... 
     def withFoo(f: Foo): ActualType
     def withBar(b: Bar): ActualType
     // ...
     def build: WhatToBuild          
 }

 class FooBarBuilder extends AbstractFooBuilder[FooBarBuilder, FooBar]
 class FooBazBuilder extends AbstractFooBuilder[FooBazBuilder, FooBaz]
 // .. etc

有很多这样的东西,我正在尝试通过以下方式减少这些 foo 的创建重复性:

def anyFoo[T <: Foo, B <: AbstractFooBuilder[B, T] : Manifest](foo: Foo, b: Bar) = manifest
  .runtimeClass
  .newInstance
  .withFoo(foo)
  .withBar(bar)
  .build

问题是,现在要创建 FooBar,我必须这样写:

 val foobar = new anyFoo[FooBar, FooBarBuilder](foo, bar)

比我想要的要长。具体来说,一旦 FooBarBuilder 类型参数已知,FooBar 是第二个参数的唯一可能性......我想知道我是否遗漏了一些技巧,这使得 "infer" 另一个参数,只需要指定一个。

有什么想法吗?

如果我正确阅读代码。该代码并不意味着此类型只有一个 AbstractFooBuilder 工厂,这留给调用者指定要使用的工厂。

如果你可以使用隐式,你可以这样做

def anyFoo[T <: Foo](foo: Foo, b: Bar)(implicit factory: AbstractFooBuilder[T]): T = {
         factory.
             withBar(b).
             withFoo(foo).
             build
}

不幸的是,拆分类型参数的标准技巧在这里不起作用,因为 B 取决于 T

但是去掉 Manifest 并简化为

怎么样?
def anyFoo[T <: Foo](builder: AbstractFooBuilder[_, T])(foo: Foo, b: Bar) = 
  builder.withBar(b).withFoo(foo).build

?你称它为anyFoo(new FooBarBuilder)(foo, b),所以推断出T,解决了你的问题。作为奖励,如果您的 class 碰巧没有默认构造函数,它会更快并且不会出现运行时错误。

如果您的方法并不总是需要创建构建器,您可以使用按名称的参数或 () => AbstractFooBuilder[_, T]

编辑:根据评论,这可行:

def mkBuilder[B <: AbstractFooBuilder[B, _] : Manifest]: B = // the complex creation code

anyFoo(mkBuilder[FooBarBuilder])(foo, b) // infers FooBar

问题是您是否可以在不访问 T 的情况下实现 mkBuilder,但是如果您的原始代码不需要 Manifest[T],那应该是可以的。

甚至

implicit class AnyFoo[T, B <: AbstractFooBuilder[B, T]](builder: B) {
  def apply(foo: Foo, b: Bar) = builder.withBar(b).withFoo(foo).build
}

mkBuilder[FooBarBuilder](foo, b)