【问题标题】:Scala: Generic class with multiple constructorsScala:具有多个构造函数的泛型类
【发布时间】:2011-07-22 11:22:32
【问题描述】:

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

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

【问题讨论】:

  • 知道了。棘手的一个,但如此明显之后我注意到了这个问题:)

标签: scala scala-2.9 multiple-constructors generics


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

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

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

    隐式转换的问题是你可能不想写 隐式 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
    

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

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

    【讨论】:

      【解决方案3】:

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

      1) 使用具有您感兴趣的构造函数的另一个类扩展您的类。注意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))
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-09-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多