首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Scala协方差,反方差混淆

Scala协方差,反方差混淆
EN

Stack Overflow用户
提问于 2018-07-10 13:02:35
回答 2查看 157关注 0票数 0

我是Scala的新手,这真的很混乱。请帮帮我。

代码语言:javascript
复制
/**
2    * Remember! In Scala, every function that takes one argument 
3    * is an instance of Function1 with signature:
4    *
5    * trait Function1[-T, +S] extends AnyRef
6    */
7   
8   class Vehicle(val owner: String)
9   class Car(owner: String) extends Vehicle(owner)
10  
11  object Printer {
12
13    val cars = List(new Car("john"), new Car("paul"))
14
15    def printCarInfo(getCarInfo: Car => AnyRef) {
16      for (car <- cars) println(getCarInfo(car))
17    }
18  }
19  
20  object Customer extends App {
21
22   val getOwnerInfo: (Vehicle => String) = _.owner
23   
24   Printer.printCarInfo(getOwnerInfo)
25  }

这段代码取自https://medium.com/@sinisalouc/variance-in-java-and-scala-63af925d21dc

规则是:

函数在输入类型中是协变的,在返回类型中是反变的,这一规则来自Liskov替换原理(LSP)。它说T是U的一个子类型,如果它支持与U相同的操作,并且它的所有操作都比U中的相应操作更少(或相同),并且提供更多(或相同)的操作(子类型是自反的,所以S <:S)。

,所以我的问题是,如果我可以传递一个子类型,其中需要一个超级类型(代码行号15和22),那么为什么下面的代码不能工作?

代码语言:javascript
复制
class MyClass extends AnyRef
class MySubClass extends MyClass

abstract class Class {
  val f1: (Any) => Any = ???
  val f2: (Any) => Boolean = ???
  val f3: (MyClass) => Any = ???
  val f4: (MySubClass) => Boolean = ???
  val f5: (Any) => Nothing = ???
  val f6: (MyClass) => Null = ???

  val f: (MyClass) => Boolean = f4; //Error
}

更新,实际上,这就像向函数传递一个param,所以我有param可以是反变量-T,返回可以是协变量+S

代码语言:javascript
复制
    class MyClass extends AnyRef
    class MySubClass extends MyClass

    abstract class Class {
      val f1: (Any) => Any = ???
      val f2: (MyClass) => Boolean = ???
      val f3: (MyClass) => Any = ???
      val f4: (MySubClass) => Boolean = ???
      val f5: (Any) => Nothing = ???
      val f6: (MyClass) => Null = ???

      val f: (MySubClass) => AnyVal = f2
}

这是一个有效的代码,因为MyClass类似于层次结构中的向上,而Boolean类似于层次结构中的cuming。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-07-10 13:17:41

让我们研究一下printCarInfo参数。接受Car作为参数并返回AnyRefgetCarInfo: Car => AnyRef的函数很少。

在scala中,只有一个参数的函数可以使用trait Function1[-T, +S] extends AnyRef来表示。

Function1参数由-T+S两种类型。第一种类型表示函数的参数。Car在我们的案例中。它是反变体(减号),这意味着你可以传递任何super-type+S表示返回类型,它是协变量(加号),这意味着您可以传递任何子类型。

调用printCarInfo并传递类型为Vehicle => String的参数。车辆是汽车的超级类型,而字符串是AnyRef的子类型,因此它满足标准。

那么,为什么参数在对变量位置,返回类型在协变量中。让我们假设论点相反:

printCarInfo接受类型函数:Vehicle=>AnyRefgetOwnerInfoCar=>AnyRef

当你试图实现printCarInfo时,我们可以处理任何争论,不仅仅是汽车,还有卡车和自行车。显然,调用Printer.printCarInfo(getOwnerInfo)失败,因为您试图从卡车或自行车getOwnerInfo,而您的方法实现只能处理汽车。希望这是清楚的。

关于你的密码。它的反面是:您可以分配f4: MysSubClass => Boolean = f,它将工作。

票数 1
EN

Stack Overflow用户

发布于 2018-07-10 13:13:29

您的代码不会编译,因为否则,例如,如果您有

代码语言:javascript
复制
class MyOtherSubClass extends MyClass

你的

代码语言:javascript
复制
val f: (MyClass) => Boolean

可以接受MyOtherSubClass作为参数,例如f(new MyOtherSubClass()),但这将调用f4(new MyOtherSubClass())。但是MyOtherSubClass不是MySubClass,所以您会调用错误类型的f4

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51265999

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档