推断类型参数

时间:2017-04-14 15:18:05

标签: scala type-inference type-parameter

我正在集成一些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

有很多这些,我试图通过这样的方式减少重复这些foos的创建:

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是第二个参数的唯一可能性......我想知道是否有一些我缺少的技巧,这将使得&#34;推断&#34;另一个参数,只需要指定一个。

有什么想法吗?

2 个答案:

答案 0 :(得分:1)

不幸的是,分割类型参数的标准技巧在这里不起作用,因为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,解决您的问题。作为奖励,如果您的类没有默认构造函数,它会更快并且没有运行时错误。

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

编辑:鉴于评论,这可能有效:

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

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

问题是,您是否可以在无法访问mkBuilder的情况下实施T,但如果您的原始代码不需要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)

答案 1 :(得分:0)

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

如果你可以使用隐含,你可以做这样的事情

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