Scala:具有多个构造函数的通用类

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

明白了。有点棘手,但是问题一旦被发现就显得非常明显 :) - Vilius Normantas
3个回答

7

new A(Some(8)) 可以是以下两种情况之一:

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

您可以显式指定类型,例如:new A[Int](Some(8))


1
问题已经被识别出来了。有没有不需要输入的解决方案?
解决方案:带有优先级的隐式转换。
隐式转换的问题在于,您可能不想编写 implicit 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]]
}

现在我们可以尝试一下(如果您使用 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

所以我们通过一点额外的代码和隐式转换获得了我们想要的行为。

我几乎可以确定还有一种类型编码方案也可以工作,但是我还没有时间完成它,而且一旦我注意到编译器坚持创建和封装用于类型限制的隐式对象(即使它只是用于类型检查),我对它失去了热情。


0

当你想要为一个泛型类创建多个构造函数时,有两种解决方法。

1)通过扩展另一个具有你感兴趣的构造函数的类来扩展你的类。注意在C[+T]中的+,它表示C0[T]是协变于C[+T]的,所以当需要C[T]时,C0[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))
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接