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