You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
traitFunction[-Arg,+Return]{
defapply(arg:Arg):Return
}
//defined trait Functionvalfoo=newFunction[Any,String]{
overridedefapply(arg:Any) :String=s"Hello ,I received a $arg"
}
//foo: Function[Any,String] = $anon$1@72dda13avalbar:Function[String,Any] = foo
//bar: Function[String,Any] = $anon$1@72dda13a
foo("test")
//res0: String = Hello ,I received a test
bar("test")
//res1: Any = Hello ,I received a test
这段代码怎么来的大家可以详细去看一下scala in depth一书,在这里就不写推导过程了,只是讨论一下为什么
什么是型变
型变在scala中灵活的分为了不变,逆变和协变
首先看个代码,这段代码来自
scala in depth
书中这段代码怎么来的大家可以详细去看一下
scala in depth
一书,在这里就不写推导过程了,只是讨论一下为什么能编译通过,以及反过来能不能,为什么不能
1
首先我们看一下Function的定义,在参数Arg位置被定义成了逆变,这个意味着我们可以在需要某个类型的时候用他的父类型替换,也就是老子换儿子,在返回值类型被定义成了协变,这就是在需要某个类型的时候用他的子类型替换,白话就是儿子换老子
2
我们看一下foo的定义,他的参数类型是Any,返回值类型是String,所以他的apply方法接收一个Any类型的参数,返回一个String类型的结果,这就是为什么
foo("test")
返回的是String类型的res03
我们再看一下bar的定义,参数类型为String,返回值类型为Any,所以如果能调用bar,需要传入String,结果是Any,也就是
bar("test")
是Any的原因4
那么为什么可以写那句
val bar:Function[String,Any] = foo
这就和型变有关了,由于Function参数位置逆变,所以需要String的时候我们能用他的老子Any替换,foo类型的参数位置正好是Any,同理返回值类型协变,需要的是Any,我们传入String自然也没错,所以可以通过编译并产生结果
那么反过来还可以么
我们试试
结果想必也猜到了
type mismatch,因为baz需要的是Any的参数和String的返回值,参数位置是逆变,只能传老子或自身类型,返回值类型是协变,只能传儿子或自身类型,你给Any传儿子String自然是不允许的
那有人就说你为什么不反过来,也就是参数位置协变,返回值类型逆变
类似
那么大事不好了,完全不能通过编译
因为参数必须逆变,而返回值必须协变,这也是有道理的
先说返回值如果逆变,那么也就是说你可以用老子代替儿子,可是儿子是String类型,如果老子是一个Int类型或者其他类型的Any类型引用,那么这里用Int取代替String明显会出问题,所以这是不被允许的
再说参数必须逆变,如果参数协变了,那么就是儿子可以代替老子,由于参数是Any类型,我们可以用String类型代替Any,可是实际上Any可能指向了其他儿子,那么儿子需要的是Int,你传入String自然也是不可以的
The text was updated successfully, but these errors were encountered: