How to properly implement `addListener { ... }`? J...
# getting-started
v
How to properly implement
addListener { ... }
? Java's example shows something like
addListener((obs, oldVal, newVal) -> { ... })
s
I don’t have experience with the framework you’re using but it seems like you’d just be able to do
Copy code
addListener { obs, oldVal, newVal ->
  ...
}
v
Autocompletion suggests
observable ->
, but later I can't call
it
inside the lambda
s
it
is only implicitly provided when you don’t declare a named parameter for the lambda
if you pass in a lambda
{ foo -> ... }
it
will not be present, the parameter will instead be named
foo
v
Got it. Still have problems with understanding lambdas
s
you’ll pick it up, they’re pretty intuitive once you’ve used them a few times
v
Is it possible in the example above to use "anonymous" lambda arguments to refer to them as
first
,
second
,
third
?
s
you can manually label them
{ first, second, third -> ... }
but Kotlin will not do it for you
moreover, it’s strongly recommended that you label your parameters with descriptive names
first, second, and third are less explanatory than
oldVal
and
newVal
, though admittedly not much less so
v
So those
first
,
second
references are not the same as
it
?
I saw in lambdas with multiple arguments
s
well, referentially they’re the same, it’s just that
it
is implicitly provided - as in, you don’t need to explicitly label it as a parameter to use it in a single-argument lambda
first
, `second`… are manually labeled
Copy code
fun foo(block: (Int, Int, Int) -> Any) { }

fun bar() {
  foo {
    println(first)
  }
}
this won’t compile
this will, however
Copy code
fun foo(block: (Int, Int, Int) -> Any) { }

fun bar() {
  foo { first, second, third ->
    println(first)
  }
}
v
I.e. I have to explicitly name arguments in lambdas with multiple ones?
And in the cases where 3 arguments are expected, compiler allow to have at least one, why? Polymorphism? As with
addListener { observer -> ... }
is still legal, as well as
addListener { obs, old, new -> ... }
Indeed... Confusing
s
I think maybe
addListener
is overloaded
you should
ctrl/cmd+b
into the definition to see if there are multiple definitions
it could be that there are two (or more) methods that take similar lambdas
Copy code
fun <T, R> addListener(block: (Observer, T, T) -> R) { ... }
fun <R> addListener(block: (Observer) -> R) { ... }
maybe something like that?
v
You're right it is so. There are implementations - one with 1 argument, another - with 3
👍 1
So to use
it
inside lambda, there should be something like
() -> Unit
in
addListener
signature, right?
But not
Observable! -> Unit
?
s
no,
() -> Unit
is a lambda that takes no arguments whatsoever and returns
Unit
you cannot use
it
for a lambda that doesn’t take arguments
to use
it
you need a lambda that takes exactly one argument (of any sort)
the return type is unimportant
v
Damn, I thought I was on the right way
s
(Observable!) -> Unit
would work just fine 👌
v
So why compiler does not allow
addListener { println("$it") }
for signature
addListener(Observable! -> Unit)
?
s
What’s the error you’re getting?
v
Overload resolution ambiguity. All these functions match
- those with 1 and 3 arguments
s
hmm, are there more than one methods that take a single-arg lambda?
if not, then this could just be a point of Java interop friction, unfortunately 😕
v
Overload resolution ambiguity
Seems so
s
actually, I’m not entirely sure
this is likely a known issue with SAM conversion
v
Ok, I'll stay with explicit argument naming (it does work), thanks for explanations
👍 1
r
@ValV I'm going to take a guess you're using JavaFX. If so, you can get all sorts of help in the #tornadofx channel.
v
Sure, but I thought that the problem was in my understanding of lambdas, 'cause used to work with C before, and did not use them in Java or Python, but Kotlin seems to use lambdas quite much
r
Fair enough
a
@Shawn @ValV Kotlin compiler needs to know the signature of the lambda before it checks the body of the lambda. In your example it cannot decide which
addListener
to call without looking at the body of the lambda
you could try to tell the compiler that the lambda will be of type
InvalidationListener
with
...addListener(InvalidationListener { println("$it") }
, maybe that works
v
@Andreas Sinz you're right trick with
InvalidationListener { ... }
does really work, but it's dark magic is beyond my imagination. I.e. the magic which is involved in choosing lambdas by the compiler
And how does it work with
InvalidationListener
? AFAIK, it's an interface with the only one method
invalidated
. I did not find where it accepts lambdas
a
@ValV because kotlin supports SAM-Conversion for java interfaces: https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions