在 Scala 中调用属于泛型类型的“静态”方法

jon*_*rry 3 scala

Scala 中有没有办法调用属于某个类型的方法?例如,假设我有一个叫做 trait 的特性Constructable,它描述了类型,而不是可以构造它们自己的默认实例。然后,我可以编写以下代码:

  trait Constructable[A] {
    def construct: A
  }

  class Foo(x: Int) extends Constructable[Foo] {
    def construct = new Foo(0)
  }

  def main(args: Array[String]) {
    val f = new Foo(4)
    println(f.construct)
  }
Run Code Online (Sandbox Code Playgroud)

这没问题,但我真正想要的是能够构造一个仅给定对象类型的默认对象。例如,假设我想接受一个可构造的列表并在列表的开头添加一个默认对象:

  def prependDefault1[A <: Constructable[A]](c: List[A]): List[A] = {
    val h = c.head
    h.construct :: c
  }
Run Code Online (Sandbox Code Playgroud)

上面的代码有效,但前提是 c 不为空。我真正想要的是写如下内容:

  def prependDefault2[A <: Constructable[A]](c: List[A]): List[A] = {
    A.construct :: c
  }
Run Code Online (Sandbox Code Playgroud)

有没有办法实现这一点,可能是通过更改 a 的定义,Constructable使该construct方法属于“类”而不是“实例”(使用 Java 术语)?

Kar*_*l S 5

你不能这样做,但你可以使用类型类来做到这一点:

trait Constructable[A] {
  def construct: A
}

// 'case' just so it's printed nicely
case class Foo(x: Int)

// implicit vals have to be inside some object; omitting it here for clarity
implicit val fooConstructable = new Constructable[Foo] {
  def construct = new Foo (0)
}

def prependDefault2[A : Constructable](c: List[A]): List[A] = {
  implicitly[Constructable[A]].construct :: c
}
Run Code Online (Sandbox Code Playgroud)

进而:

scala> prependDefault2(Nil: List[Foo])
res7: List[Foo] = List(Foo(0))
Run Code Online (Sandbox Code Playgroud)

一些最后的评论:

隐式必须存在于对象内部。它可以位于三个位置:

  • object Constructable { implicit val fooConstructable = ... (类型类特征的伴随对象)

  • object Foo { implicit val fooConstructable = ... (我们为其实现类型类的类的伴随对象)

  • object SomethingElse { implicit val fooConstructable = ... (一些随机的无关对象)

只有在最后一种情况下您才需要使用import SomethingElse._,以便能够使用隐式。