分享

scala中的型变问题

学习到scala中的型变问题,搞不懂?????

型变中的协变和型变中的逆变?使用场景?如何选择?怎么使用??
对于函数参数是逆变点、返回值是协变点?

具体如何理解、学习型变中的逆变、协变。如何很好的理解它。

已有(3)人评论

跳转到指定楼层
w123aw 发表于 2018-5-8 19:46:18
首先比较正式的解释:
逆变与协变用来描述类型转换(type transformation)后的继承关系,其定义:如果A、B表示类型,f(⋅)表示类型转换,≤表示继承关系(比如,A≤B表示A是由B派生出来的子类);
  • f(⋅)是逆变(contravariant)的,当A≤B时有f(B)≤f(A)成立;
  • f(⋅)是协变(covariant)的,当A≤B时有f(A)≤f(B)成立;
  • f(⋅)是不变(invariant)的,当A≤B时上述两个式子均不成立,即f(A)与f(B)相互之间没有继承关系。


下面在比较白话的解释:
就像
爹A,儿子B
这时候有一个泛型类,给他穿上马甲
List<A>,List<B>。那么他们是什么关系?
1.如果List<A>是爹,List<B>是儿子,那么这就是协变,我觉得这里理解为顺变比较好。
下面就要看好了。
如果我雷个去。这时候
2.List<B>变成是爹,List<A>变成了儿子,娘的反天了,这就是逆变了。应该我们所谓的逆子,不孝顺。
相信协变和逆变,应该了解了。




回复

使用道具 举报

w123aw 发表于 2018-5-8 19:49:16
明白了概念,他们到底有什么用,这时候可能涉及到类型兼容,赋值。看下面程序
[mw_shl_code=scala,true]package fineqtbull.customer  
//出版物类  
class Publication(val title: String)  
//书籍类  
class Book(title: String) extends Publication(title)  
//图书库类  
object Library {  
    //定义图书库内所有的书籍  
    val books: Set[Book] =  
        Set(  
            new Book("Programming in Scala"),  
            new Book("Walden")  
        )  
    //打印所有图书内容,使用外部传入的函数来实现  
    def printBookList(info: Book => AnyRef) {  
        //确认Scala中一个参数的函数实际上是Function1特征的实例  
        assert(info.isInstanceOf[Function1[_, _]])  
        //打印  
        for (book <- books)  
            println(info(book))  
    }  
    //打印所有图书内容,使用外部传入的GetInfoAction特征的实例来实现  
    def printBokkListByTrait[P >: Book, R <: AnyRef](  
            action : GetInfoAction[P, R]) {  
        //打印  
        for (book <- books)  
            println(action(book))  
    }  
  
}  
//取得图书内容特征,P类型参数的类型下界是Book,R类型参数的类型上界是AnyRef  
trait GetInfoAction[P >: Book, R <: AnyRef] {  
    //取得图书内容的文本描述,对应()操作符  
   def apply(book : P) : R  
}  
//单例对象,文件的主程序  
object Customer extends Application {  
    //定义取得出版物标题的函数  
    def getTitle(p: Publication): String = p.title  
    //使用函数来打印  
    Library.printBookList(getTitle)  
  
    //使用特征GetInfoAction的实例来打印  
    Library.printBokkListByTrait(new GetInfoAction[Publication, String] {  
            def apply(p: Publication) : String = p.title })  
}  [/mw_shl_code]

上例的Library单例对象的printBookList方法使用了函数来取得书籍的内容。在Scala中函数也是对象,上述情况下的函数有一个参数,实际上该参数是如下特征的实例。

[mw_shl_code=scala,true]trait Function1[-S, +T] {  
  def apply(x: S): T  
}  [/mw_shl_code]
printBookList的info参数是Function1类型,而 Function1的-S类型参数是逆变,+T参数是协变。【这里看到-S和+T可能不明白,这里是Scala中规定的。】printBookList方法的assert(info.isInstanceOf[Function1[_, _]])语句可以验证这一点。从printBookList方法的定义可以知道,info的S类型参数是Book,T类型参数是AnyRef。然而主函数中使用处则是Library.printBookList(getTitle),getTitle函数中对应的S是Publication,T是String为什么可以与printBookList原来的定义不一致呢,这就是协变和逆变的威力了。由于-S是逆变,而Publication是Book的父类,所以Publication可以代替(泛化为)Book由于+T是协变,而String是AnyRef的子类,所以String可以代替(泛化为)AnyRef。如此一来,主程序的语句也就完全正确了。


参考
http://fineqtbull.iteye.com/blog/477994
https://www.cnblogs.com/en-heng/p/5041124.html

回复

使用道具 举报

不可替代 发表于 2018-5-10 08:49:39
w123aw 发表于 2018-5-8 19:49
明白了概念,他们到底有什么用,这时候可能涉及到类型兼容,赋值。看下面程序
[mw_shl_code=scala,true]pa ...

解释到位,感谢,又学习到了,[url=]玫瑰[/url]非常感谢。[url=]玫瑰[/url]
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条