sealed abstract class List[+A] ... { // 忽略了混入的trait
...
def map[B](f: A => B): List[B] = {...}
...
}
这里方法map的类型参数B是不能使用变异标记的,如果你修改其变异标记,则会返回编译错误。
函数的参数和返回值
现在我们讨论scala中函数参数的一个非常重要的结论:函数的参数必须是逆变的,而返回值必须是协变的
为什么呢?
接下来我们考虑scala内置的带一个参数的函数类型Function1,其简化的定义如下:
trait Function1[-T1, +R] extends AnyRef { self =>
/** Apply the body of this function to the argument.
* @return the result of function application.
*/
def apply(v1: T1): R
...
override def toString() = "<function1>"
}
我们知道类似 A=>B 的形式在scala中是可以自动被转换为Function1的形式。
scala> var f: Int=>Int = i=>i+1
f: Int => Int = <function1>
实际上其会被转换成为如下的形式:
val f: Int => Int = new Function1[Int,Int] {
def apply(i: Int): Int = i + 1
}
假如我们定义了三个class 如下:
class CSuper { def msuper() = println("CSuper") }
class C extends CSuper { def m() = println("C") }
class CSub extends C { def msub() = println("CSub") }
我们可以定义如下几个f:
var f: C => C = (c: C) => new C // ➋
f = (c: CSuper) => new CSub // ➌
f = (c: CSuper) => new C // ➍
f = (c: C) => new CSub // ➎
f = (c: CSub) => new CSuper // ➏ 编译错误!
根据Function1[-T1, +R]的定义,2-5可以通过编译,而6会编译失败。
怎么理解6呢? 这里我们要区分两个概念,函数的定义类型和函数的运行类型。
这里f的定义类型是 C=>C。 当f = (c: CSub) => new CSuper时,它的实际apply方法就是:
scala> trait MyFunction2[+T1, +T2, -R] {
| def apply(v1:T1, v2:T2): R = ???
| }
<console>:37: error: contravariant type R occurs in covariant position
in type (v1: T1, v2: T2)R of method apply
def apply(v1:T1, v2:T2): R = ???
^
<console>:37: error: covariant type T1 occurs in contravariant position
in type T1 of value v1
def apply(v1:T1, v2:T2): R = ???
^
<console>:37: error: covariant type T2 occurs in contravariant position
in type T2 of value v2
def apply(v1:T1, v2:T2): R = ???
^
scala> class ContainerPlus[+A](var value: A)
<console>:34: error: covariant type A occurs in contravariant position
in type A of value value_=
class ContainerPlus[+A](var value: A)
^
scala> class ContainerMinus[-A](var value: A)
<console>:34: error: contravariant type A occurs in covariant position
in type => A of method value
class ContainerMinus[-A](var value: A)
通过上面的例子,我们也可以得到一个结论,可变参数化类型是不能变异的。
假如可变参数是协变的ContainerPlus[+A],那么对于:
val cp: ContainerPlus[C]=new ContainerPlus(new CSub)