我正在尝试编写一些需要执行以下操作的Scala代码:
class Test[Type] {
def main {
SomeFunc classOf[Type]
val testVal: Type = new Type()
}
}
它失败了。我显然不了解Scala泛型参数。显然,误解是在C ++中,模板本质上就像字符串替换一样,所以只要传入的类具有默认构造函数,新的Type()就可以工作。但是,在Scala中,类型是不同类型的对象。
答案 0 :(得分:28)
正如您所指出的,C ++有模板。简而言之,C ++说“对于所有类型的T都有测试,以便测试编译。”这样可以很容易地在T上隐式添加约束,但是在不利的一面,它们是隐含的,并且如果不阅读代码,您的类的用户可能很难理解。
Scala的参数多态(又名泛型)更像ML,Haskell,Java和C#。在Scala中,当你写“class Test [T]”时,你说“对于所有T,存在一个没有约束的类型Test [T]”。这在形式上更容易推理,但它确实意味着你必须明确约束。例如,在Scala中你可以说“class Test [T<:Foo]”来说T必须是Foo的子类型。
C#有一种方法可以向T添加关于构造函数的约束,但不幸的是Scala没有。
有几种方法可以解决Scala中的问题。一个是类型安全的,但更冗长。另一种不是类型安全的。
类型安全的方式看起来像
class Test[T](implicit val factory : () => T) {
val testVal = factory
}
然后,您可以拥有适用于您的系统类型的工厂体
object Factories {
implicit def listfact[X]() = List[X]()
implicit def setfact[X]() = Set[X]()
// etc
}
import Factories._
val t = new Test[Set[String]]
如果您图书馆的用户需要自己的工厂,那么他们可以添加他们自己的工厂对象。这个解决方案的一个优点是可以使用任何带工厂的东西,无论是否有无参数构造函数。
不那么类型安全的方法使用反射和Scala中的一个名为manifest的功能,这是一种绕过类型擦除的Java约束的方法
class Test[T](implicit m : Manifest[T]) {
val testVal = m.erasure.newInstance().asInstanceOf[T]
}
使用此版本,您仍然可以写
class Foo
val t = new Test[Foo]
但是,如果没有no-arg构造函数可用,则会得到运行时异常而不是静态类型错误
scala> new Test[Set[String]]
java.lang.InstantiationException: scala.collection.immutable.Set
at java.lang.Class.newInstance0(Class.java:340)