【问题标题】:How can I invoke the constructor of a Scala abstract type?如何调用 Scala 抽象类型的构造函数?
【发布时间】:2011-09-30 22:27:59
【问题描述】:

我正在尝试弄清楚如何为 Scala 抽象类型调用构造函数:

class Journey(val length: Int)
class PlaneJourney(length: Int) extends Journey(length)
class BoatJourney(length: Int) extends Journey(length)

class Port[J <: Journey] {
  def startJourney: J = {
    new J(23) // error: class type required but J found
  }
}

这甚至可行吗?我熟悉Scala manifests,但我不清楚他们如何在这里提供帮助。同样,我不知道如何对伴随对象的 apply() 构造函数做同样的事情:

object Journey { def apply() = new Journey(0) }
object PlaneJourney { def apply() = new PlaneJourney(0) }
object BoatJourney { def apply() = new BoatJourney(0) }

class Port[J <: Journey] {
  def startJourney: J = {
    J() // error: not found: value J
  }
}

感谢您的任何想法!

【问题讨论】:

    标签: scala


    【解决方案1】:

    没有直接的方法来调用构造函数或访问仅给定类型的伴随对象。一种解决方案是使用构造给定类型的默认实例的类型类。

    trait Default[A] { def default: A }
    
    class Journey(val length: Int)
    object Journey {
      // Provide the implicit in the companion
      implicit def default: Default[Journey] = new Default[Journey] {
        def default = new Journey(0)
      }
    }
    
    class Port[J <: Journey : Default] {
      // use the Default[J] instance to create the instance
      def startJourney: J = implicitly[Default[J]].default
    }
    

    您需要向支持创建默认实例的类的所有伴随对象添加隐式 Default 定义。

    【讨论】:

    • 感谢 Moritz - 但是将代码粘贴到 REPL 中会引发一些错误?另外,我如何将参数添加到默认的“构造函数”?
    • 您必须进入粘贴模式才能使此代码在 REPL 中工作(只需在粘贴前输入 :paste)。 Philippe 修复的代码中也存在错误。
    • 如果你想添加参数,你可以简单地在Default trait 中添加一个新方法。 implicitly[Default[J]] 会给你一个带有类型参数的 trait 实例,你可以调用任何你喜欢的方法,例如implicitly[Default[J]].create(23)。有关隐式如何工作的详细信息,请参见 this question
    • 感谢 Moritz - 感谢您花时间解释这一切。当您解决我提出的所有问题时,将其标记为最佳答案!
    【解决方案2】:

    您的类需要一个隐式构造函数参数来获取Manifest。然后你可以调用erasure获取Class并调用newInstance,如果有空构造函数,它会反射调用。

    class J[A](implicit m:Manifest[A]) {
      def n = m.erasure.newInstance()
    }
    
    new J[Object].n
    

    从 Scala 2.10 开始,清单中的 erasure 属性已被弃用。 def n = m.runtimeClass.newInstance() 做同样的事情,但没有警告。

    【讨论】:

    • 这种方式不需要无参构造函数吗?
    • @Chris 确实如此,这就是限制。除非您为所需的任何工厂制作特征。
    • 感谢 Kim - 很高兴能清楚地解释 newInstance() “构造函数”
    【解决方案3】:

    我的倾向是无法做到这一点。我远非 Scala 大师,但我的理由是:

    1. 您有一个带有类型参数 T 的类 Port,其中 T 必须从 Journey 继承(但 T 不必完全是 Journey,这很重要)。
    2. 在 Port 中,您定义了一个创建新 T 的方法。此类不知道 T 是什么,因此不知道 T 的构造函数是什么样的。
    3. 因为您不知道 T 的构造函数接受哪些参数,所以您不知道要传递给它的参数。

    这个问题的解决方案在另一个问题中处理得很好,所以我会为他们指出而不是在这里重复:Abstract Types / Type Parameters in Scala

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-09-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-09
      相关资源
      最近更新 更多