Scala:具有多个构造函数的泛型类

时间:2011-07-22 11:22:33

标签: scala scala-2.9 multiple-constructors generics

我正在尝试创建一个这样的泛型类:

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

3 个答案:

答案 0 :(得分:7)

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

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

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

答案 1 :(得分:1)

问题已经确定。那些不需要输入的解决方案呢?

解决方案:优先隐式转化。

隐式转换的问题在于您可能不想写   隐式def everything_is_optional [A](a:A)=一些(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]]
}

现在我们可以尝试一下(如果您使用REPL,请务必在:paste模式下输入上述内容,或者在对象内输入并导入对象,以便object X被解释作为class X的伴随对象:

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

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

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

我几乎肯定有一种类型编码方案也可以使用,但我没有时间完成它,加上我失去了对它的热情一旦我注意到编译器坚持创建和装箱隐含的使用对于这种方案中的类型边界,即使它只需要进行类型检查。

答案 2 :(得分:0)

当您需要通用类的几种构造时,有两种解决方法。

1)用具有您感兴趣的构造函数的另一个类扩展您的类。请注意+中的C[+T],这意味着C0[T]与{{1}是协变的},以便在需要C[+T]时接受C0[T]。至少最经常检查这个协方差。

C[T]

2)使用一种方法,例如,您可以方便地将其放入伴随对象中。在Scala中这很惯用。

class C[+T](i: Int)

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