调用"静态函数" Scala中的泛型类型

时间:2018-02-08 00:42:50

标签: scala generics

我试图在Scala中表示另一种语言的片段。我希望所有类都有一个表示其类型的自定义String值。当谈到集装箱类时,我遇到了麻烦。

如何根据typeName的通用参数?

抓取MyContainerClass[T <: MyType]
object MyLang {

  trait MyType {
    protected val typeName: String
  }

  class MyString(val underlying: String) extends MyType {
    protected val typeName = "mystring"
  }

  class MyInt(val underlying: Int) extends MyType {
    protected val typeName = "myint"
  }

  class MyContainerClass[T <: MyType](val underlying: Seq[T]) extends MyType {
    // LINE THAT OBVIOUSLY DOESN'T WORK
    val typeName = T.typeName + "[]"
  }
}

我的第一个想法是抓住underlying的元素并在其上调用typeName,但如果Seq[T]Nil会怎样?

我的第二个想法是为所有扩展MyType的类创建一个0参数构造函数,这样我就可以调用new T().typeName但这看起来并不麻烦,并且没有办法强制执行所有类型的子类型都包含具有特定签名的构造函数。

从这里我发现了Manifest和TypeTags。我看到它们与我的问题有什么关系,但却没有看到它们如何帮助我构建解决方案!

3 个答案:

答案 0 :(得分:5)

这种事情通常使用类型类来完成:

trait TypeName[T] {
  def typeName: String
}

implicit object MyString extends TypeName[MyString] {
  def typeName = "mystring"
}

implicit object MyInt extends TypeName[MyInt] {
  def typeName = "myint"
}

class MyString(val underlying: String) extends MyType {
  val typeName = MyString.typeName
}
class  MyInt(val underlying: Int) extends MyType {
  val typeName = MyInt.typeName
}

class MyContainerClass[T <: MyType : TypeName](
  val underlying: Seq[T]
) extends MyType {
  val typeName = implicitly[TypeName[T]].typeName + "[]"
}

这个想法是,无论何时创建MyContainerClass,都会查找TypeName的相应实例并隐式传递给构造函数,因此您可以在那里访问它并检索名称。

答案 1 :(得分:1)

我认为@Dima的答案很棒,而我实际上是在编写相同的代码,但他却打败了我。所以我会提供另一种解决方案。如果你可以使用真实的类名:

import scala.reflect._

object MyLang {

  trait MyType {
    val typeName = this.getClass.getName
  }

  class MyString(val underlying: String) extends MyType

  class MyInt(val underlying: Int) extends MyType

  class MyContainerClass[T <: MyType](val underlying: Seq[T])
    (implicit tag: ClassTag[T])
    extends MyType {
    override val typeName = s"${getClass.getName}[${tag.runtimeClass.getName}]"
  }
}

以下是它的印刷品:

scala> new MyLang.MyContainerClass(Seq(new MyLang.MyString("abc"))).typeName

res0: String = MyLang$MyContainerClass[MyLang$MyString]

您甚至可以将typeName定义为元组(parent class, child class)或任何其他包含java.lang.Class[_]的非字符串数据类型。

问题实际上是Scala不允许您覆盖静态方法,因为它没有它们。您可以在某种程度上使用object方法模拟它们。

在您的情况下,您尝试使用实例变量typeName但是您无法从类中获取它,因为它不是实例。在这种情况下,你需要一些地方来保存这个值,如@Dima的答案 - 你根据类型隐式查找的对象。缺点是你必须为每种类型创建它们。

答案 2 :(得分:0)

对于只寻找接受答案的最小实现的任何人,只需定义一些特征Y[T],一个类Z和一个扩展{{ 1}}:

Z