理想情况下,我希望能够在Scala中执行以下操作:
import Builders._
val myBuilder = builder[TypeToBuild] // Returns instance of TypeToBuildBuilder
val obj = myBuilder.methodOnTypeToBuildBuilder(...).build()
原则上,目标只是能够使用外部映射定义(即假设无法更改这些类)和“杠杆”将TypeToBuild
“映射”到TypeToBuildBuilder
这在类型推理中。
我使用AnyRef
类型进行了以下操作:
import Builders._
val myBuilder = builder(TypeToBuild)
myBuilder.methodOnTypeToBuildBuilder(...).build()
object Builders {
implicit val typeToBuildBuilderFactory =
new BuilderFactory[TypeToBuild.type, TypeToBuildBuilder]
def builder[T, B](typ: T)(implicit ev: BuilderFactory[T, B]): B = ev.create
}
class BuilderFactory[T, B: ClassTag] {
def create: B = classTag[B].runtimeClass.newInstance().asInstanceOf[B]
}
请注意,类型作为函数参数而不是类型参数传递。
如果您想了解如何使用Any
类型而不仅仅是AnyRef
类型,我会非常高兴。似乎这种限制是因为Singleton类型仅支持AnyRef
s(即我使用TypeToBuild.type)。
话虽如此,一个解决原始“理想”场景的答案(使用类型参数而不是函数参数)将是太棒了!
修改
需要classOf[_]
的可能解决方案(真的不需要使用classOf
!):
import Builders._
val myBuilder = builder(classOf[TypeToBuild])
myBuilder.methodOnTypeToBuildBuilder(...).build()
object Builders {
implicit val typeToBuildBuilderFactory =
new BuilderFactory[classOf[TypeToBuild], TypeToBuildBuilder]
def builder[T, B](typ: T)(implicit ev: BuilderFactory[T, B]): B = ev.create
}
class BuilderFactory[T, B: ClassTag] {
def create: B = classTag[B].runtimeClass.newInstance().asInstanceOf[B]
}
能够使用builder(TypeToBuild)
真的只是优雅/简洁的胜利。能够使用builder[TypeToBuild]
会很酷,因为这可能有一天可以工作(在Scala中使用类型推断进展):
val obj: TypeToBuild = builder.methodOnTypeToBuildBuilder(...).build();
以下是使用classOf
:http://ideone.com/94rat3
答案 0 :(得分:2)
是的,Scala支持基于参数类型的返回类型。这方面的一个示例是集合API中的方法,如map
,它使用CanBuildFrom
类型类来返回所需的类型。
我不确定你要对你的示例代码做什么,但也许你想要这样的东西:
trait Builder[-A, +B] {
def create(x: A): B
}
object Builders {
implicit val int2StringBuilder = new Builder[Int, String] {
def create(x: Int) = "a" * x
}
def buildFrom[A, B](x: A)(implicit ev: Builder[A, B]): B = ev.create(x)
}
import Builders._
buildFrom(5)
newInstance
的魔力仅适用于具有不带参数的构造函数的具体类,因此它可能不够通用,无法使用。
答案 1 :(得分:1)
如果你不害怕隐含的转换,你可以这样做:
import scala.language.implicitConversions
trait BuilderMapping[TypeToBuild, BuilderType] {
def create: BuilderType
}
case class BuilderSpec[TypeToBuild]()
def builder[TypeToBuild] = BuilderSpec[TypeToBuild]
implicit def builderSpecToBuilder[TypeToBuild, BuilderType]
(spec: BuilderSpec[TypeToBuild])
(implicit ev: BuilderMapping[TypeToBuild, BuilderType]) = ev.create
case class Foo(count: Int)
case class FooBuilder() {
def translate(f: Foo) = "a" * f.count
}
implicit val FooToFooBuilder = new BuilderMapping[Foo, FooBuilder] {
def create = FooBuilder()
}
val b = builder[Foo]
println(b.translate(Foo(3)))
隐式转换并不太糟糕,因为它们被限制在这些面向构建器的类型中。需要进行转换才能使b.translate
有效。
看起来像wingedsubmariner的答案是你想要的大部分,但你不想指定TypeToBuild
和BuilderType
(你没有必要想传递一个值)。为实现这一目标,我们需要将单个通用签名分解为两个部分,这就是BuilderSpec类型存在的原因。
也可以使用部分通用应用程序之类的东西(参见a question that I asked earlier的答案),尽管我现在无法将这些内容放在我的脑海中。
答案 2 :(得分:0)
我会回答我自己的问题,因为Redditor最终给了我正在寻找的答案,他们似乎选择不回复。
trait Buildable[T] {
type Result
def newBuilder: Result
}
object Buildable {
implicit object ABuildable extends Buildable[A] {
type Result = ABuilder
override def newBuilder = new ABuilder
}
implicit object BBuildable extends Buildable[B] {
type Result = BBuilder
override def newBuilder = new BBuilder
}
}
def builder[T](implicit B: Buildable[T]): B.Result = B.newBuilder
class ABuilder {
def method1() = println("Call from ABuilder")
}
class BBuilder {
def method2() = println("Call from BBuilder")
}
然后你会得到:
scala> builder[A].method1()
Call from ABuilder
scala> builder[B].method2()
Call from BBuilder
您可以在此处查看reddit帖子:http://www.reddit.com/r/scala/comments/2542x8/is_it_possible_to_define_a_function_return_type/
这里有一个完整的工作版本:http://ideone.com/oPI7Az