我试图在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。我看到它们与我的问题有什么关系,但却没有看到它们如何帮助我构建解决方案!
答案 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