4

我正在尝试创建一个像这样的通用类:

class A[T](v: Option[T]) {
  def this(v: T) = this(Some(v))
  def this() = this(None)
  def getV = v 
}

然后我做了一些测试:

scala> new A getV
res21: Option[Nothing] = None
scala> new A(8) getV
res22: Option[Int] = Some(8)

到现在为止还挺好。但是一旦我尝试调用主构造函数,我就会得到:

scala> new A(Some(8)) getV
<console>:9: error: ambiguous reference to overloaded definition,
both constructor A in class A of type (v: T)A[T]
and  constructor A in class A of type (v: Option[T])A[T]
match argument types (Some[Int])
       new A(Some(8)) getV
       ^

scala> new A(None) getV
<console>:9: error: ambiguous reference to overloaded definition,
both constructor A in class A of type (v: T)A[T]
and  constructor A in class A of type (v: Option[T])A[T]
match argument types (None.type)
       new A(None) getV
       ^

这两个构造函数之间有什么“模棱两可”的?或者(让我猜猜)这是我对 Scala 的类型系统不了解的另一件事?:)

当然,如果我使用非泛型类,一切都会按预期工作。我的B课工作得很好:

class B(v: Option[String]) {
  def this(v: String) = this(Some(v))
  def this() = this(None)
  def getV = v 
}

scala> new B("ABC") getV
res26: Option[String] = Some(ABC)
scala> new B getV
res27: Option[String] = None
scala> new B(Some("ABC")) getV
res28: Option[String] = Some(ABC)
scala> new B(None) getV
res29: Option[String] = None
4

3 回答 3

7

new A(Some(8))可以是:

  • A[Int]通过主构造函数的新实例,
  • A[Option[Int]]通过备用构造函数的新实例。

您可以明确指定类型,例如new A[Int](Some(8)).

于 2011-07-22T11:34:05.553 回答
1

问题已经确定。不需要打字的解决方案呢?

解决方案:具有优先级的隐式转换。

隐式转换的问题是您可能不想编写隐式 def everything_is_optional[A](a: A) = Some(a) 因为这会破坏您的选项类型系统(因为您会在不注意的情况下获得提升) . 也许你想要这个,但就个人而言,我喜欢类型系统在我对某事是否是一个选项感到困惑时告诉我。所以我们需要某种其他的包装器。像这样:

// Might want to do this for more than just one class, so generalize
class Implicator[A,B](val value: A) {
  def to[C] = this.asInstanceOf[Implicator[A,C]]
}

class X[A](i: Implicator[Option[A],X[A]]) {
  private val v = i.value
  def getV = v
}
trait LowPriorityX {
  implicit def everything_for_x[A](a: A) = new Implicator(Option(a)).to[X[A]]
}
object X extends LowPriorityX {
  implicit def option_for_x[A](oa: Option[A]) = new Implicator(oa).to[X[A]]
}

:paste现在我们可以尝试一下(如果使用 REPL,请确保在 mode 中输入上述内容,或者将其全部输入到对象中并导入该对象,以便将object X其解释为伴随对象class X

scala> new X(5)
res0: X[Int] = X@26473f4c

scala> new X(Some(5))
res1: X[Int] = X@1d944379

因此,我们以一些额外的代码和隐式转换为代价获得了我们想要的行为。

我几乎肯定有一个类型编码方案也可以工作,但我还没有时间完成它,而且一旦我注意到编译器坚持创建和装箱用于类型边界的隐式,我就失去了对它的热情在这样的方案中,即使它只需要用于类型检查。

于 2011-07-22T19:42:01.247 回答
0

当您想要一个泛型类的多个构造时,有两种解决方法。

1)用另一个具有您感兴趣的构造函数的类扩展您的类。注意+in C[+T],这意味着它与C0[T]是协变的C[+T],因此在需要C0[T]时将被接受C[T]。至少在大多数情况下,检查一下这个协方差的东西。

class C[+T](i: Int)

class C0[T](s:String) extends C[T](Integer.parseInt(s))

2)使用一种方法,例如,您可以方便地放入伴随对象中。这在 Scala 中相当地道。

object C {
  def apply[T](s:String) = new C[T](Integer.parseInt(s))
}
于 2019-09-06T19:41:18.103 回答