I am a newcomer to Scala and it's really very confusion. Please help me.
/**
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 }
This code is taken from https://medium.com/@sinisalouc/variance-in-java-and-scala-63af925d21dc
The Rule is:
This rule of function being covariant in its input type and contravariant in its return type comes from the Liskov substitution principle (LSP). It says that T is a subtype of U if it supports the same operations as U and all of its operations require less (or same) and provide more (or same) than the corresponding operations in U (subtyping is reflexive so S <: S).
So my question is if I can pass a subtype where a super type is required (above code line number 15 and 22) then why the following code does not works?
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
}
Update So actually it's like passing a param to the function so I have param can be contravariant [-T] and return can be covariant [+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
}
This is a valid code as MyClass
is like going Up in the hierarchy and Boolean
is like cuming down in the hierarchy.
Your code won't compile because otherwise eg if you had a
class MyOtherSubClass extends MyClass
your
val f: (MyClass) => Boolean
could accept a MyOtherSubClass
as a parameter eg f(new MyOtherSubClass())
, but that would be calling f4(new MyOtherSubClass())
. But MyOtherSubClass
isn't a MySubClass
, so you'd be calling f4
with the wrong type
Lets looks into printCarInfo
parameter. It's little function that accept Car
as an argument and returns AnyRef
: getCarInfo: Car => AnyRef
.
In scala such functions with one argument can be represented using trait Function1[-T, +S] extends AnyRef
Function1
parametrised by -T
and +S
two types. First type represent argument of function. Car
in our case. And it's contra-variant (minus sign), which means you can pass any super-type
. +S
represents return type and it's covariant (plus sign), which means you can pass any sub-type.
printCarInfo
invoked and parameter of type Vehicle => String
passed. Vehicle is super type for Car and String is subtype for AnyRef, so it satisfy criteria.
So why arguments are in contra variant position and return type in covariant. Let's try to assume opposite for arguments:
printCarInfo
accept function of type: Vehicle=>AnyRef
and getOwnerInfo
is Car=>AnyRef
When you trying to implement printCarInfo we can process any argument, not just cars, but lorries and bicycles. Obviously invocation of Printer.printCarInfo(getOwnerInfo)
fails as you're trying to getOwnerInfo from lorry or bicycle, while your method implementation can deal with cars only. Hopefully it's clear.
So regarding your code. It's opposite: you can assign f4: MysSubClass => Boolean = f
and it will work.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.