我有一对看起来像这样的类(class)。有一个 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))
}
你会注意到
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
这很好,可以处理
String
隐式类型,因为我们使用的是 GeneratorFactory
.问题
现在当我想构建一个
Generator
时问题就出现了无需经过工厂。我希望能够做到这一点:val g2 = Generator('a', 'b', 'c')
g2.generate // error
但我收到一个错误,因为
g2
有类型 Generator[Char,Nothing]
和 Scala“无法基于类型为 Nothing 的集合构造具有类型为 Char 的元素的类型为 Nothing 的集合。”我想要的是一种告诉 Scala
S
的“默认值”的方法。类似于 Seq[T]
而不是 Nothing
.借用默认参数的语法,我们可以将其视为如下内容:case class Generator[T, S=Seq[T]]
解决方案不足
当然,如果我们明确告诉生成器它生成的类型应该是什么,它就可以工作,但我认为默认选项会更好(我的实际场景更复杂):
val g3 = Generator[Char, String]('a', 'b', 'c')
val o3 = g3.generate // works fine, o3 has type String
我想过重载
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
期望输出
我想要的是一种简单地构建
Generator
的方法。不指定类型 S
并将其默认为 Seq[T]
这样我就可以:val g2 = Generator('a', 'b', 'c')
val o2 = g2.generate
// o2 is of type Seq[Char]
我认为这对用户来说是最干净的界面。
有什么想法可以让我做到这一点吗?
最佳答案
您是否有理由不想使用基本特征然后缩小范围 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]
}
}
我已经做了
S
一种抽象类型,以使其更不受用户影响,但您也可以将其设为类型参数。
关于Scala:指定默认泛型类型而不是 Nothing,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11811267/