16

我正在尝试编写一些需要执行以下操作的 Scala 代码:

class Test[Type] { 
   def main {
       SomeFunc classOf[Type]
       val testVal: Type = new Type()
    }
 }

它失败了。我显然不了解 Scala 泛型参数。显然,误解是在 C++ 中,模板的功能本质上类似于字符串替换,因此只要传入的类具有默认构造函数, new Type() 就可以工作。然而,在 Scala 中,类型是不同种类的对象。

4

1 回答 1

28

正如您所指出的,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)
于 2009-05-04T14:27:27.473 回答