我是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中的相应操作更少(或相同),并且提供更多(或相同)的操作(子类型是自反的,所以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
}更新,实际上,这就像向函数传递一个param,所以我有param可以是反变量-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类似于层次结构中的cuming。
发布于 2018-07-10 13:17:41
让我们研究一下printCarInfo参数。接受Car作为参数并返回AnyRef:getCarInfo: Car => AnyRef的函数很少。
在scala中,只有一个参数的函数可以使用trait Function1[-T, +S] extends AnyRef来表示。
Function1参数由-T和+S两种类型。第一种类型表示函数的参数。Car在我们的案例中。它是反变体(减号),这意味着你可以传递任何super-type。+S表示返回类型,它是协变量(加号),这意味着您可以传递任何子类型。
调用printCarInfo并传递类型为Vehicle => String的参数。车辆是汽车的超级类型,而字符串是AnyRef的子类型,因此它满足标准。
那么,为什么参数在对变量位置,返回类型在协变量中。让我们假设论点相反:
printCarInfo接受类型函数:Vehicle=>AnyRef和getOwnerInfo是Car=>AnyRef
当你试图实现printCarInfo时,我们可以处理任何争论,不仅仅是汽车,还有卡车和自行车。显然,调用Printer.printCarInfo(getOwnerInfo)失败,因为您试图从卡车或自行车getOwnerInfo,而您的方法实现只能处理汽车。希望这是清楚的。
关于你的密码。它的反面是:您可以分配f4: MysSubClass => Boolean = f,它将工作。
发布于 2018-07-10 13:13:29
您的代码不会编译,因为否则,例如,如果您有
class MyOtherSubClass extends MyClass你的
val f: (MyClass) => Boolean可以接受MyOtherSubClass作为参数,例如f(new MyOtherSubClass()),但这将调用f4(new MyOtherSubClass())。但是MyOtherSubClass不是MySubClass,所以您会调用错误类型的f4
https://stackoverflow.com/questions/51265999
复制相似问题