正如您所指出的,C++ 有模板。简而言之,C++ 说“对于所有类型 T 都有一个 Test 可以编译 Test”。这使得在 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 中称为清单的特性,这是一种绕过 Java 类型擦除约束的方法
class Test[T](implicit m : Manifest[T]) {
val testVal = m.erasure.newInstance().asInstanceOf[T]
}
使用此版本,您仍然可以编写
class Foo
val t = new Test[Foo]
但是,如果没有可用的无参数构造函数,您将获得运行时异常而不是静态类型错误
scala> new Test[Set[String]]
java.lang.InstantiationException: scala.collection.immutable.Set
at java.lang.Class.newInstance0(Class.java:340)