But `(Any) -> Unit` is a subtype of `(String) -...
# announcements
k
But
(Any) -> Unit
is a subtype of
(String) -> Unit
, so you're supposed to be able the always replace the latter with the former if something is taking it.
g
It's other way round. Liskov is not broken here, btw. The principle is about accepting subtypes and @robstoll asked about restricting supertype.
k
While
Any
is a supertype of
String
,
(Any) -> Unit
is a subtype of
(String) -> Unit
.
g
Why so, I'd like to inform myself more on this? Even if true, the posted function's signature was accepting B, the subtype of A. So, it's not a Liskov case. It would be Liskov if you declare to accept A, but can't handle B. Than it's a violation, but it wasn't the case here.
k
First about the Liskov case, I'm saying that
(Any) -> Unit
is a subtype of
(String) -> Unit
, so functions accepting
(String) -> Unit
should also accept
(Any) -> Unit
.
We're running into a bit of recursion here too, because it looks like subtype is defined in term of the subtitution principle. I'll write an example.
Copy code
interface A
interface B: A

fun f(a: A) {}
fun g(b: B) {}

val value: B = ...
g(value)
Whenever you call
g
you can always replace that with a call to
f
, right? Because
f
simply accepts whatever
g
does and more.
Similarly,
Copy code
val func: (B) -> Unit = ::f
val value: B = ...
func(value)
is perfectly okay.
Ie. you can always substitute a function
(B) -> Unit
by a function
(A) -> Unit
, which makes
(A) -> Unit
a subtype of
(B) -> Unit
.
Another way to think about it: it is more difficult ie. specific to accept more than to accept only a single type.
r
You can also think about overriding a method in a subclass. You are allowed to specify a less specific type for parameters (contravariance) and more specific types for return types (covariance)