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

bsd*_*ish 16 types scala

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

class Test[Type] { 
   def main {
       SomeFunc classOf[Type]
       val testVal: Type = new Type()
    }
 }
Run Code Online (Sandbox Code Playgroud)

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

Jam*_*Iry 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
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以拥有一系列工厂,用于您系统中有用的类型

object Factories {
  implicit def listfact[X]() = List[X]()
  implicit def setfact[X]() = Set[X]()
  // etc
}

import Factories._
val t = new Test[Set[String]]
Run Code Online (Sandbox Code Playgroud)

如果您的库的用户需要他们自己的工厂,那么他们可以添加他们自己的工厂对象的等价物.这个解决方案的一个优点是可以使用任何带工厂的东西,无论是否有无参数构造函数.

不那么类型安全的方法使用反射和Scala中的一个名为manifest的功能,这是一种绕过类型擦除的Java约束的方法

 class Test[T](implicit m : Manifest[T]) {
   val testVal = m.erasure.newInstance().asInstanceOf[T]
 }
Run Code Online (Sandbox Code Playgroud)

有了这个版本,你仍然可以写

class Foo
val t = new Test[Foo]
Run Code Online (Sandbox Code Playgroud)

但是,如果没有no-arg构造函数可用,则会得到运行时异常而不是静态类型错误

scala> new Test[Set[String]] 
java.lang.InstantiationException: scala.collection.immutable.Set
at java.lang.Class.newInstance0(Class.java:340)
Run Code Online (Sandbox Code Playgroud)