scala学习手记22 - 参数化类型的可变性
java 的代码中多少有些不是很严谨的内容,比如下面的这段代码:
public class Trouble {
public static void main(String[] args) {
Integer[] arr1 = new Integer[2];
arr1[0] = new Integer(1);
Object[] arr2 = arr1;
arr2[1] = new Double(2.0);
for (Integer i : arr1) {
System.out.println(i);
}
}
}
这段代码是可以通过编译的。但是在执行的时候会报错。

使用scala编写类似的代码(印象中类似的代码之前也提到过):
var arr1:Array[Int] = new Array[Int](3)
var arr2:Array[Any] = arr1
这段程序在编译期就会报错:因为不能将显式声明为Int型的数组赋给Any类型的数组。看一下报错信息:

将子类对象赋值给超类称为协变;将超类对象赋值给子类称为逆变。默认情况下,scala对这两种“变”都不支持。
不过有时候真的很需要协变的支持——不然继承的实现价值会小很多。看一下下面的代码:
class Pet(val name: String) {
override def toString() = name
}
class Dog(override val name: String) extends Pet(name)
class Cat(override val name: String) extends Pet(name)
def playWithPets(pets: Array[Pet]) {println("play with " + pets.mkString(","))}
在上面的代码里定义了一个playWithPets方法,因为我们既想遛狗又想逗猫,有事没事还想和海豚一起游泳。如果不能支持协变,那怎么办,总不能写PlayWithDog、PlayWithCat、PlayWithDelphin、PlayWithXXX…无穷匮也了。
先勉强试试下面的代码:
val dogs = Array(new Dog("Rover"), new Dog("Comet"))
playWithPets(dogs)
根据前面的经验,这段代码在编译期就会报错:

看吧,遛狗都不行,别说逗猫调戏海豚什么的了。
既然说到这里了,那么肯定会有解决的方法,修改下playWithPets方法:
def playWithPets[T <: Pet](pets: Array[T]){}
这里用特殊的语法定义了playWithPets()。T<:Pet表示T所代表的类派生自Pet。通过这样的语法,我们告诉scala,我们需要的参数是Pet类型的数组或者继承自Pet类型的类的数组,换句话说就是为参数类型指定了一个上界,就是。这样调整后再编译执行就没问题了:

还有另一个特殊的语法“P>:Dog”。很明显的,可以类推出来这是为参数类型指定了一个下界。举个例子:偷猫逗狗结束了,需要把猫狗关进笼子里,可是没有猫笼子了,狗笼还有富余,那怎么办,把猫关进狗笼子啊(宠物都是单间待遇)。示例代码如下:
def sendToDogCaf[T >: Dog](pet: Pet) {
println("send " + pet + "to Dog's cafe")
}
sendToDogCaf(new Cat("Lily"))
sendToDogCaf(new Pet("Rabbit"))
执行结果如下图:

就是这样,上面演示了如何在方法定义里控制方法的参数。
不过,如果是GM的话,当然也可以用杂兵下副本——如果是容器类的作者,当然也可以控制参数类型的可变性。如下面这段代码:
class MyList[+T]//... var list1 = new MyList[int]
var list2: MyList[Any] = null
list2 = list1 // OK
这里,+T告诉Scala允许协变;换句话说,在类型检查期间,让Scala接收某个类型或者其基类型。这样,就可以将MyList[Int]赋给MyList[Any]。对于Array[Int]而言,这是不可以的。不过,对于List(Scala程序库中实现的函数式List)而言,这是可以的。
类似的,参数化类型用-T替换T,就可以让Scala支持类型逆变。
###########
scala学习手记22 - 参数化类型的可变性的更多相关文章
- scala学习手记17 - 容器和类型推断
关于scala的类型推断前面已经提到过多次.再来看一下下面这个例子: import java.util._ var list1: List[Int] = new ArrayList[Int] var ...
- scala学习手记38 - 方法命名约定和for表达式
方法命名约定 之前在学习<运算符重载>一节时曾经说过一个方法命名约定:方法的第一个字符决定了方法的优先级.现在再说另一个命名约定:如果方法以冒号(:)结尾,则调用目标是运算符后面的实例. ...
- scala学习手记23 - 函数值
scala的一个最主要的特性就是支持函数编程.函数是函数编程中的一等公民:函数可以作为参数传递给其他函数,可以作为其他函数的返回值,甚至可以在其它函数中嵌套.这些高阶函数称为函数值. 举一个简单的例子 ...
- scala学习手记8 - 自适应的默认做法
scala有一些默认做法,会让代码更简洁.更易读写,下面列出了这样几个特性: 1. 支持脚本.scala支持脚本,因此无须将所有的代码都放到类里.如果脚本可以满足需求,就将代码放到一个脚本里,无须再创 ...
- Scala学习手记1 - 快速体验
又重新开始了scala的学习,因为中断了太长时间,所以这次还得从零开始.学习的过程就记录在这个博客上了. 这次学习的教程是<scala程序设计 java虚拟机多核编程实战>,我在多看上买了 ...
- scala学习手记40 - 使用case类
前面两节我们已经多次接触过case关键字了.case关键字不仅可以用在match/case中来执行模式匹配,也可以用来修饰类.不过用case修饰的类也主要是用来做模式匹配.在上一节曾经提到过match ...
- scala学习手记40 - case表达式里的模式变量和常量
再来看一下之前的一段代码: def process(input: Any) { input match { case (a: Int, b: Int) => println("Proc ...
- scala学习手记39 - 模式匹配
在java中有switch/case这样的模式匹配语句,可以匹配的类型包括int,byte,char,short, enum,在java8又支持了字符串. 在scala中也有类似的模式匹配语句,即ma ...
- scala学习手记37 - 容器的使用
这次统一看一下scala中容器类的几个方法. Set filter()方法 filter()方法用来从Set中过滤获取含有指定特征的元素.示例代码如下: val colors1 = Set(" ...
随机推荐
- 【Trello】使用指南
一.谷歌浏览器插件推荐: Card Color Titles for Trello 在Card上现实标签的标题文字 Trellists: Trello Lists Master 允许展示和隐藏List ...
- Express入门教程:一个简单的博客
来自: http://ourjs.com/detail/56b2a6f088feaf2d031d2468 Express 简介 Express 是一个简洁而灵活的 node.js Web应用框架, ...
- 个人觉得存成char(12),优于varchar(12)
w 延展一点:0----(还是上边的url),varchar(10)则数据库的存储1-11bytes,而不是0-10bytes;varchar(256)则为2-258bytes; 1----如果待入库 ...
- Spring Data HelloWorld(三)
在 Spring Data 环境搭建(二) 的基础之上 我们改动 http://www.cnblogs.com/fzng/p/7253068.html 定义个一个接口 继承Repository类 ...
- 看用Tornado如何自定义实现表单验证
我们知道,平时在登陆某个网站或软件时,网站对于你输入的内容是有要求的,并且会对你输入的错误内容有提示,对于Django这种大而全的web框架,是提供了form表单验证功能,但是对于Tornado而言, ...
- Web 编程中路径问题
web.xml 中 <url-pattern> 路径(即 Servlet 路径) 要么以 "*" 开头, 要么以 "/" 开头. 转发和包含路径(服 ...
- 兼容获取元素当前样式 currentStyle || getComputedStyle
function getStyle(ele, attr) { return ele.currentStyle ? ele.currentStyle[attr] : window.getComputed ...
- 流畅的python 使用一等函数实现设计模式
案例分析:重构“策略”模式 经典的“策略”模式 电商领域有个功能明显可以使用“策略”模式,即根据客户的属性或订单中的商品计算折扣.假如一个网店制定了下述折扣规则. 有 1000 或以上积分的顾客,每个 ...
- 浅谈HTML文档模式
不知道爱多想的你有没有在编写HTML代码时思考过 <!DOCTYPE html> 或是这一长串看都看不懂的 <!DOCTYPE HTML PUBLIC "-//W3C//D ...
- 通过SSRS创建动态分组报表的方法!
SSRS是微软专门的报表开发工具,对于一般高级用户(非专业开发人员)可以通过SQL Server Report Builder创建,可以把制作好的发布在单独部署的SQL Server Reportin ...