Scala中的抽象类型/类型参数

时间:2009-05-04 06:33:27

标签: scala types

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

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

它失败了。我显然不了解Scala泛型参数。显然,误解是在C ++中,模板本质上就像字符串替换一样,所以只要传入的类具有默认构造函数,新的Type()就可以工作。但是,在Scala中,类型是不同类型的对象。

1 个答案:

答案 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)