Scala:指定默认泛型类型而不是 Nothing

标签 scala generics

我有一对看起来像这样的类(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 的值,但没有类型 SGenerator 存储.

我们可以像这样使用这些类。工厂按照 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/

相关文章:

scala - 如何在 play2 中获取已发布的 "id=1&id=2"?

scala - IntelliJ 上的 SBT 需要很长时间才能刷新

scala - Spark : produce RDD[(X, X)] 来自 RDD[X] 的所有可能组合

c# - 使用 2 列的 LINQ JOIN 的通用扩展方法

java - 如何在 java 中使用基本方法实现通用 PriorityQueue?

泛型类的 Java 8 模糊方法引用

java - 如何检查 Java 代码中的 Scala Option 类型是否为 None

Swift:使用符合相同协议(protocol)的嵌套枚举案例的代码更少

typescript - 从键值对推断类型,其中值是函数签名?

scala - Flink 的 JDBC 接收器失败并出现不可序列化错误