我是Scala的新手,这真的很混乱。请帮助我。
/**
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相同的操作,并且其所有操作所需的操作更少(或相同) 并提供比U中相应操作更多(或相同)的信息 (子类型是自反的,所以S <:S)。
所以我的问题是,如果我可以传递需要超级类型的子类型(代码行号15和22以上),那么为什么以下代码不起作用?
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
}
更新 因此,实际上就像将参数传递给函数一样,这样我可以使参数具有协变性[-T]和返回可以协变[+ S]
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
就像在层次结构中向下移动。
答案 0 :(得分:1)
您的代码将无法编译,因为例如如果您有
class MyOtherSubClass extends MyClass
您的
val f: (MyClass) => Boolean
可以接受MyOtherSubClass
作为参数,例如f(new MyOtherSubClass())
,但这将调用f4(new MyOtherSubClass())
。但是MyOtherSubClass
不是MySubClass
,因此您将以错误的类型呼叫f4
答案 1 :(得分:1)
让我们调查printCarInfo
参数。接受Car
作为参数并返回AnyRef
:getCarInfo: Car => AnyRef
的小函数。
在scala中,可以使用trait Function1[-T, +S] extends AnyRef
Function1
由-T
和+S
两种类型参数化。第一种类型表示函数的参数。 Car
。它是反变数(负号),表示您可以传递任何super-type
。 +S
表示返回类型,它是协变量(加号),表示您可以传递任何子类型。
printCarInfo
,并传递类型Vehicle => String
的参数。
Vehicle是Car的超级类型,而String是AnyRef的子类型,因此它满足条件。
那么,为什么参数在变体位置和返回类型在协变中处于相反的位置。让我们假设参数相反:
printCarInfo
接受类型为Vehicle=>AnyRef
且getOwnerInfo
为Car=>AnyRef
的函数
当您尝试实现printCarInfo时,我们可以处理任何参数,不仅是汽车,还包括卡车和自行车。
显然,Printer.printCarInfo(getOwnerInfo)
的调用失败,因为您试图从货车或自行车上获取所有者信息,而您的方法实现只能处理汽车。
希望很清楚。
因此,关于您的代码。相反:您可以分配f4: MysSubClass => Boolean = f
,它将起作用。