我有一对看起来像这样的课程.有一个Generator基于某些类级别值生成一个值,并GeneratorFactory构造一个Generator.
case class Generator[T, S](a: T, b: T, c: T) {
def generate(implicit bf: CanBuildFrom[S, T, S]): S =
bf() += (a, b, c) result
}
case class GeneratorFactory[T]() {
def build[S <% Seq[T]](seq: S) = Generator[T, S](seq(0), seq(1), seq(2))
}
Run Code Online (Sandbox Code Playgroud)
你会注意到GeneratorFactory.build接受一个类型的参数S并Generator.generate产生一个类型的值S,但是没有类型S存储的类型Generator.
我们可以使用这样的类.工厂按顺序工作Char,并generate产生一个String因为build给出了一个String.
val gb = GeneratorFactory[Char]()
val g = gb.build("this string")
val o = g.generate
Run Code Online (Sandbox Code Playgroud)
这很好,并且String隐式处理类型,因为我们正在使用GeneratorFactory.
现在问题出现了,当我想要建造一个Generator没有通过工厂.我希望能够做到这一点:
val g2 = Generator('a', 'b', 'c')
g2.generate // error
Run Code Online (Sandbox Code Playgroud)
但是我得到一个错误,因为g2有类型Generator[Char,Nothing]和Scala"无法使用类型为Nothing的集合构造类型为Char的元素类型的集合."
我想要的是一种告诉Scala"默认值"的S方法,Seq[T]而不是Nothing.借用默认参数的语法,我们可以将其视为:
case class Generator[T, S=Seq[T]]
Run Code Online (Sandbox Code Playgroud)
当然,如果我们明确地告诉生成器它的生成类型应该是什么,它是有效的,但我认为默认选项会更好(我的实际场景更复杂):
val g3 = Generator[Char, String]('a', 'b', 'c')
val o3 = g3.generate // works fine, o3 has type String
Run Code Online (Sandbox Code Playgroud)
我认为重载Generator.apply有一个通用类型的版本,但这会导致错误,因为显然Scala无法区分这两个apply定义:
object Generator {
def apply[T](a: T, b: T, c: T) = new Generator[T, Seq[T]](a, b, c)
}
val g2 = Generator('a', 'b', 'c') // error: ambiguous reference to overloaded definition
Run Code Online (Sandbox Code Playgroud)
我想要的是一种简单地构造一个Generator没有指定类型的方法,S并将其默认为Seq[T]以便我可以这样做:
val g2 = Generator('a', 'b', 'c')
val o2 = g2.generate
// o2 is of type Seq[Char]
Run Code Online (Sandbox Code Playgroud)
我认为这将是用户最干净的界面.
我有什么想法可以实现这一目标吗?
您是否有理由不想使用基本特征然后S根据需要缩小其子类?以下例如符合您的要求:
import scala.collection.generic.CanBuildFrom
trait Generator[T] {
type S
def a: T; def b: T; def c: T
def generate(implicit bf: CanBuildFrom[S, T, S]): S = bf() += (a, b, c) result
}
object Generator {
def apply[T](x: T, y: T, z: T) = new Generator[T] {
type S = Seq[T]
val (a, b, c) = (x, y, z)
}
}
case class GeneratorFactory[T]() {
def build[U <% Seq[T]](seq: U) = new Generator[T] {
type S = U
val Seq(a, b, c, _*) = seq: Seq[T]
}
}
Run Code Online (Sandbox Code Playgroud)
我已经制作了S一个抽象类型,以使其更加偏离用户的方式,但您也可以将其作为类型参数.