不可替代 发表于 2018-5-8 09:10:28

scala中的型变问题

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

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

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

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

明白了概念,他们到底有什么用,这时候可能涉及到类型兼容,赋值。看下面程序
package fineqtbull.customer
//出版物类
class Publication(val title: String)
//书籍类
class Book(title: String) extends Publication(title)
//图书库类
object Library {
    //定义图书库内所有的书籍
    val books: Set =
      Set(
            new Book("Programming in Scala"),
            new Book("Walden")
      )
    //打印所有图书内容,使用外部传入的函数来实现
    def printBookList(info: Book => AnyRef) {
      //确认Scala中一个参数的函数实际上是Function1特征的实例
      assert(info.isInstanceOf])
      //打印
      for (book <- books)
            println(info(book))
    }
    //打印所有图书内容,使用外部传入的GetInfoAction特征的实例来实现
    def printBokkListByTrait(
            action : GetInfoAction) {
      //打印
      for (book <- books)
            println(action(book))
    }

}
//取得图书内容特征,P类型参数的类型下界是Book,R类型参数的类型上界是AnyRef
trait GetInfoAction {
    //取得图书内容的文本描述,对应()操作符
   def apply(book : P) : R
}
//单例对象,文件的主程序
object Customer extends Application {
    //定义取得出版物标题的函数
    def getTitle(p: Publication): String = p.title
    //使用函数来打印
    Library.printBookList(getTitle)

    //使用特征GetInfoAction的实例来打印
    Library.printBokkListByTrait(new GetInfoAction {
            def apply(p: Publication) : String = p.title })
}

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

trait Function1[-S, +T] {
def apply(x: S): T
}
printBookList的info参数是Function1类型,而 Function1的-S类型参数是逆变,+T参数是协变。【这里看到-S和+T可能不明白,这里是Scala中规定的。】printBookList方法的assert(info.isInstanceOf])语句可以验证这一点。从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
明白了概念,他们到底有什么用,这时候可能涉及到类型兼容,赋值。看下面程序
pa ...

解释到位,感谢,又学习到了,玫瑰非常感谢。玫瑰
页: [1]
查看完整版本: scala中的型变问题