Scala中有个概念称为类型参数,本质上就相当于JAVA中的泛型。泛型的目的是限制集合中的类型,保证程序的健壮性。在Scala中其应用场景也是一样的,可以在集合、类、函数的定义中提供类型参数,保证逻辑中相关联的类型是一致的。
因此在Scala中应用泛型类型的类,也称之为泛型类。在这些类的内部,比如属性或方法中的参数变量,约定并统一类型限制。对于创建的泛型类,在进行创建并实例化时,可以将类型参数替换为实际的类型。Scala具有自动推断泛型类型的功能,当对泛型对象属性或方法赋值传值时,Scala会自动执行类型推断。先看下面的示例:
class Element[T]{
def validateType(element:T): Unit = {
element match {
case e1: String => printf("Type=%s", e1.getClass)
case e2: Int => printf("Type=%s", e2.getClass)
case e3: Boolean => printf("Type=%s", e3.getClass)
case _ => println("Not match")
}
}
}
var e1 = new Element[String]
e1.validateType("message")
// 输出:Type=class java.lang.String
var e2 = new Element[Int]
e2.validateType(10)
// 输出:Type=classjava.lang.Integer
var e3 = new Element[Long]
e3.validateType(100) // Not match
了解泛型类之后,再来看看泛型函数。与泛型类类似,所谓泛型函数是指在声明函数时指定泛型类型。函数体中的变量或者返回值就可以使用泛型类型来声明并进行强制性类型限制。和泛型类一样,可以直接给泛型函数传递值,并由Scala自动推断泛型的实际类型;同样也可以在调用函数时,手动指定泛型类型。
// 以下泛型函数可以指定入参类型
def assertType[T](obj: T) : Unit = {
obj match {
case i: Int => printf("Type is Int[%s]", i.getClass)
case s: String => printf("Type is String[%s]",s.getClass)
case b: Boolean => printf("Type is Boolean[%s]",b.getClass)
}
}
print(assertType[String]("stringObj"))
// 返回:Type is String[class java.lang.String]()
print(assertType[Int](9081))
// 返回:Type is Int[class java.lang.Integer]()
print(assertType[Boolean](false))
// 返回:Type is Boolean[class java.lang.Boolean]()
边界(Bounds)
在使用泛型时通常会约定其边界,比如约定为某个类型的子类型(上边界)或父类型(下边界),这在Scala开发中是一个非常有效的特性。先来看一个上边界的示例:
class Node(val ip: String) {
def run(): Unit = {
printf("Node[%s] is running.\n", ip)
}
}
class WorkNode(ip : String) extendsNode(ip)
class NodeManager[T <: Node] {
def deploy(node: T): Unit = {
printf("Preparing deploy node[%s].\n", node.ip)
node.run()
}
}
var nm = new NodeManager[WorkNode]()
var node = newWorkNode("10.221.78.1")
nm.deploy(node)
// 输出:Preparing deploy node[10.221.78.1].
// Node[10.221.78.1] is running.
除了指定泛型类型的上边界,也可以对下边界进行指定,即传入的泛型类必须是指定类的父类型,这种方式相对使用场景较少,不做赘述。还有一种场景需要判断传入的泛型类是否在某个边界范围内,此时可以使用View Bounds对上下边界的进行强化,其核心是定义隐式转换方法来处理类型,示例如下:
class Node(val ip: String) {
var namespace = "default"
def destroy(): Unit ={
printf("Namespace[%s] - Node[%s] is destroyed.\n", namespace,ip)
}
}
class WorkNode(ip: String) extendsNode(ip) {}
class TemporaryNode(val ip: String) {
var namespace = "system"
def destroy(): Unit ={
printf("Namespace[%s] - Node[%s] is destroyed.\n", namespace,ip)
}
}
// 对类型进行隐式转换,将TemporaryNode隐式转换为WorkNode
implicit def convertToNode(t: Object): WorkNode = {
if(t.isInstanceOf[TemporaryNode]) {
val _tmp = t.asInstanceOf[TemporaryNode]
var w = new WorkNode(_tmp.ip)
w.namespace = _tmp.namespace
return w
} else
Nil
}
class NodeManager[R <% Node] {
def expel(node: R): Unit = {
printf("Preparing expel node[%s].\n", node.ip)
node.destroy()
}
}
var nm = new NodeManager[TemporaryNode]()
var node = newTemporaryNode("10.221.78.1")
nm.expel(node)
// 输出:Preparing expel node[10.221.78.1].
// Namespace[system] - Node[10.221.78.1]is destroyed.
泛型数组
如果要在Scala中实例化泛型数组,则必须用到Manifest Context Bounds。如果数组元素类型为T,需要为类或者函数定义[T:Manifest]泛型类型,才能对泛型数组作实例化。
def collectNodes[T: Manifest] (nodes: T*):Array[T] = {
val array = new Array[T](nodes.length)
for(idx <- 0 until nodes.length) {
array(idx) = nodes(idx)
}
array
}
var worknodes = List(newWorkNode("10.10.0.1"),
new WorkNode("10.10.0.2"))
var tempnodes = List(newTemporaryNode("10.0.0.77"),
new TemporaryNode("10.0.0.78"))
var array = collectNodes(worknodes).++:(collectNodes(tempnodes))
协变和逆变
Scala中的协变和逆变特性非常有特色,一般用于解决泛型类型的继承关系。举个例子,假设有泛型类Protocol和HttpProtocol,两者是父子继承关系;与此同时有类型Server,那么Server[Protocol]与Server[HttpProtocol]间的可以通过这一特性实现类似的继承关系。可以使用符合+T来约定协变类型。
class Protocol
class HttpProtocol extends Protocol
class Server[+T] (val ip: String) // 对于Server约定协变类型
// 此时支持Protocol类型及其子类的Server泛型可作为参数传入
def deploy(server: Server[Protocol]): Unit= {
printf("Preparing deploy server[%s]", server.ip)
}
deploy(new Server[HttpProtocol]("10.0.11.20"))
// Preparing deploy server[10.0.11.20]
逆变相当于定义下限,用符号-T约定逆变支持的泛型子类型:
class Server[-T] (val ip: String) // 对于Server约定逆变类型
// 此处支持HttpProtocol类及其父类泛型
def deploy(server: Server[HttpProtocol]):Unit = {
printf("Preparing deploy server[%s]", server.ip)
}
deploy(newServer[Protocol]("10.0.12.39"))
// 返回Preparing deploy server[10.0.12.39]
原文作者:江玮
版权声明:本文版权归作者(译者)所有,欢迎转载,但未经作者(译者)同意必须保留此段声明,且在文章页面明显位置给出,本文链接如有问题,可留言咨询。