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 - 参数化类型的可变性的更多相关文章

  1. scala学习手记17 - 容器和类型推断

    关于scala的类型推断前面已经提到过多次.再来看一下下面这个例子: import java.util._ var list1: List[Int] = new ArrayList[Int] var ...

  2. scala学习手记38 - 方法命名约定和for表达式

    方法命名约定 之前在学习<运算符重载>一节时曾经说过一个方法命名约定:方法的第一个字符决定了方法的优先级.现在再说另一个命名约定:如果方法以冒号(:)结尾,则调用目标是运算符后面的实例. ...

  3. scala学习手记23 - 函数值

    scala的一个最主要的特性就是支持函数编程.函数是函数编程中的一等公民:函数可以作为参数传递给其他函数,可以作为其他函数的返回值,甚至可以在其它函数中嵌套.这些高阶函数称为函数值. 举一个简单的例子 ...

  4. scala学习手记8 - 自适应的默认做法

    scala有一些默认做法,会让代码更简洁.更易读写,下面列出了这样几个特性: 1. 支持脚本.scala支持脚本,因此无须将所有的代码都放到类里.如果脚本可以满足需求,就将代码放到一个脚本里,无须再创 ...

  5. Scala学习手记1 - 快速体验

    又重新开始了scala的学习,因为中断了太长时间,所以这次还得从零开始.学习的过程就记录在这个博客上了. 这次学习的教程是<scala程序设计 java虚拟机多核编程实战>,我在多看上买了 ...

  6. scala学习手记40 - 使用case类

    前面两节我们已经多次接触过case关键字了.case关键字不仅可以用在match/case中来执行模式匹配,也可以用来修饰类.不过用case修饰的类也主要是用来做模式匹配.在上一节曾经提到过match ...

  7. scala学习手记40 - case表达式里的模式变量和常量

    再来看一下之前的一段代码: def process(input: Any) { input match { case (a: Int, b: Int) => println("Proc ...

  8. scala学习手记39 - 模式匹配

    在java中有switch/case这样的模式匹配语句,可以匹配的类型包括int,byte,char,short, enum,在java8又支持了字符串. 在scala中也有类似的模式匹配语句,即ma ...

  9. scala学习手记37 - 容器的使用

    这次统一看一下scala中容器类的几个方法. Set filter()方法 filter()方法用来从Set中过滤获取含有指定特征的元素.示例代码如下: val colors1 = Set(" ...

随机推荐

  1. IDEA 设置代码模板

    一.代码模板 参考: IntelliJ IDEA 使用(一)基本设置与类.方法模板设置 - 云 + 社区 - 腾讯云 文件代码模板的使用 - IntelliJ IDEA 使用教程 - 极客学院 Wik ...

  2. 一起做RGB-D SLAM调试

    最近在学习高博的一起做RGB-D SLAM第一版本,其中调试出现了挺多问题,百度查找许多资料, 最后调通所有程序,记录以下运行环境. 高博一起做RGB-D SLAM系列主页: http://www.c ...

  3. fineReport---sql

    一.开窗函数-逐层平均 在创建数据集时用sql的开窗排名函数[AVG(字段) over(PARTITION BY 分组字段 order by 逐层字段)]处理,然后进行直接调用. 详细说明 二.开窗函 ...

  4. Spoken English Practice(not always estimating your status in other's hearts. you will lose yourself when you live in other's look. do your best and walk on you own way.)

    绿色:连读:                  红色:略读:               蓝色:浊化:               橙色:弱读     下划线_为浊化 口语蜕变(2017/7/8) 英 ...

  5. dot 使用笔记

    Graphviz (英文:Graph Visualization Software的缩写)是一个由AT&T实验室启动的开源工具包,用于绘制DOT语言脚本描述的图形 sudo apt-get i ...

  6. pre 布局

    w

  7. root Permission denied

    w 遇见现象,原因待查

  8. The Log: What every software engineer should know about real-time data's unifying abstraction

    http://engineering.linkedin.com/distributed-systems/log-what-every-software-engineer-should-know-abo ...

  9. 百度 验证码识别API 使用

    先到百度云申请文字识别API ,会给你一个API KEY和一个SECRET KEY,免费,一天最多500次请求. try: temp_url = 'https://aip.baidubce.com/o ...

  10. js中的整除运算

      Math.ceil(count / pagesize); //向上整除 4/3=2;   Math.floor(count / pagesize); //向下整除 4/3=1; Math.roun ...