Scala:指定一个默认泛型类型,而不是什么都不指定

Scala:指定一个默认泛型类型,而不是什么都不指定,scala,generics,Scala,Generics,我有两个类似这样的类。有一个生成器根据某些类级别的值生成值,还有一个生成器工厂构造一个生成器 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:

我有两个类似这样的类。有一个
生成器
根据某些类级别的值生成值,还有一个
生成器工厂
构造一个
生成器

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))
}
这很好,可以隐式处理
字符串
类型,因为我们使用的是
生成器工厂

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))
}

问题 现在,当我想在不经过工厂的情况下构建一个
生成器时,问题就出现了。我希望能够做到这一点:

val g2 = Generator('a', 'b', 'c')
g2.generate // error
但是我得到了一个错误,因为g2
具有类型
生成器[Char,Nothing]
并且Scala“无法使用基于Nothing类型集合的Char类型元素构造Nothing类型集合。”

我想要的是告诉Scala,
S
的“默认值”类似于
Seq[T]
而不是
Nothing
。借用默认参数的语法,我们可以将其视为:

case class Generator[T, S=Seq[T]]

解决方案不足 当然,如果我们显式地告诉生成器其生成的类型应该是什么,它是有效的,但是我认为默认选项会更好(我的实际场景更复杂):

我考虑过重载
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

期望输出 我想要的是一种简单构造
生成器
的方法,无需指定类型
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]
  }
}
导入scala.collection.generic.CanBuildFrom
特征发生器[T]{
S型
def a:T;def b:T;def c:T
def generate(隐式bf:CanBuildFrom[S,T,S]):S=bf()+=(a,b,c)结果
}
对象生成器{
def应用[T](x:T,y:T,z:T)=新发电机[T]{
类型S=序列[T]
val(a,b,c)=(x,y,z)
}
}
案例类生成器工厂[T](){

def build[U一个选项是将
canbuild的召唤从
移动到一个可以帮助确定
S
的地方(或者说,它的实例)

case class Generator[T,S](a: T, b: T, c: T)(implicit bf: CanBuildFrom[S, T, S]) {
  def generate : S =
    bf() += (a, b, c) result
}
示例REPL会话

scala> val g2 = Generator('a', 'b', 'c')
g2: Generator[Char,String] = Generator(a,b,c)

scala> g2.generate
res0: String = abc
更新

GeneratorFactory
也必须修改,以便其
build
方法将适当的
CanBuildFrom
实例传播到
Generator
构造函数

case class GeneratorFactory[T]() {
  def build[S](seq: S)(implicit conv: S => Seq[T], bf: CanBuildFrom[S, T, S]) =
    Generator[T, S](seq(0), seq(1), seq(2))
}

并不是说Scala<2.10.0不能在同一个方法定义中混合视图边界和隐式参数列表,所以我们必须转换绑定的
S Seq[t]

这并没有直接回答您的主要问题,因为我认为其他人正在处理这个问题。相反,这是对您请求类型参数的默认值的响应

我对此做了一些思考,甚至开始写一个提议,要求对语言进行修改以允许它。然而,当我意识到“无”的真正来源时,我停止了。它不是我预期的某种“默认值”。我将尝试解释它的来源


为了将类型分配给类型参数,Scala使用了最具体的可能/合法类型。因此,例如,假设您有“class a[T](x:T)”并且您说“new a[Int]”。您直接为T指定了“Int”值。现在假设您说的是“new a(4)”.Scala知道4和T必须具有相同的类型。4可以具有介于“Int”和“Any”之间的任何类型。在该类型范围内,“Int”是最具体的类型,因此Scala创建了一个“a[Int]”。现在假设您说“new a[AnyVal]”.现在,您正在寻找最具体的类型T,这样Int就可以使用apply方法创建
SimpleGenerator
ComplexGenerator
。这对于直接构造
Generator
,非常有用,但不幸的是,它会导致“没有足够的参数用于方法apply”
GeneratorFactory.build
@dhg:您可以通过添加一个隐式参数来修复该部分:
def build[S](seq:S)(隐式conv:S=>seq[T],cbf:CanBuildFrom[S,T,S])=…
。您不能只编写
生成器(1,2,3)
仍然是一个问题。感谢您的更新。我假设这适用于Char/String情况,因为predef中有一个隐式的
Seq[Char]=>String
CanBuildFrom?不幸的是,即使找不到隐式的CanBuildFrom,我也需要它工作(如
生成器(1,2,3)
)我喜欢这个解决方案,因为它涵盖了所有情况,不会增加太多的复杂性。需要注意的是,将
S
设置为类型参数意味着
generate
具有正确的返回类型(字符串、Seq[Char]等),而不是像
g.S
这样的返回类型。
case class GeneratorFactory[T]() {
  def build[S](seq: S)(implicit conv: S => Seq[T], bf: CanBuildFrom[S, T, S]) =
    Generator[T, S](seq(0), seq(1), seq(2))
}