0

I can't understand why the following code works: Sam Interface:

    fun interface WebResponseHandler
{
    fun onWebResponseFinished(jsonObject:JSONObject)
}

and Implemnetaiton looks like:

  val onWebResponseHandler: VolleyHandler.WebResponseHandler = VolleyHandler.WebResponseHandler()
{
    fun onWebResponseFinished(jsonObject: JSONObject) {
        Log.v("da", "dsa")
    }

    fun foo() {
        it.getString("name")
    }
}

How does foo function knows what is it? After all, foo function isn't part of the VolleyHandler.WebResponseHandler interface, and it works only when interface as only one method.Function Foo is being called without any arguments, so why is the meaning of it there?

In addition another problem occurs if I'm omitting the keyword fun from the interface declaration, when i'm trying to implement the interface:

val onWebResponseHandler: VolleyHandler.WebResponseHandler = VolleyHandler.WebResponseHandler()
{
    fun onWebResponseFinished(jsonObject: JSONObject) {
        Log.v("da", "dsa")
    }

    fun foo() {
        Log.v("something", "something")

    }
}

I'm getting a compilation error saying

Interface WebResponseHandler does not have constructors

what is the different between both situation? If i remove the () i'm getting a lot of different errors like :

Function declaration must have a name

I hope someone can figure out what is wrong here

Eitanos30
  • 1,331
  • 11
  • 19
  • What's happening is very sutle and weird, but none of this code is doing what you think it is. What you need to _actually_ do what you think you're doing is to write `object :` after the `=` sign: `= object : VolleyHandler.WebResponseHandler() { ... }` – Louis Wasserman Nov 11 '20 at 20:41
  • @LouisWasserman, can i use the fact the it's a *SAM* interface? Do you have explanation to all the issues i have written in the post? it seems that Kotlin doesn't have explanation for all the mess – Eitanos30 Nov 11 '20 at 20:46
  • It seems that if i use **()** i must open the { at the same line. Really can't understand what is going here – Eitanos30 Nov 11 '20 at 20:52

2 Answers2

2

It's not Kotlin that's a mess, it's that you're using the wrong syntax for what you want to do.

What fun interface lets you do is write

val onWebResponseHandler = VolleyHandler.WebResponseHandler {
    jsonObject: JSONObject -> Log.v("da", "dsa")
}

What you can do with a normal interface is write

val onWebResponseHandler = object : VolleyHandler.WebResponseHandler() {
    fun onWebResponseFinished(jsonObject: JSONObject) {
        Log.v("da", "dsa")
    }

    fun foo() {
        Log.v("something", "something")

    }
}

Your code does none of those things, and when compiled, doesn't do anything like what you mean it to. What you wrote is equivalent to

val onWebResponseHandler = VolleyHandler.WebResponseHandler() { 
  unusedJsonObject: JSONObject ->
    fun aFunctionWhichIsDefinedAndNeverCalled(jsonObject: JSONObject) {
        Log.v("da", "dsa")
    }

    fun anotherFunctionWhichIsDefinedAndNeverCalled() {
        unusedJsonObject.getString("name")
    }
    // doesn't actually do anything at all when executed
    // just defines functions which aren't ever invoked
}

...which compiles because, since it's a fun interface, you can just pass a lambda to the constructor, but doesn't actually do anything resembling what you expect.

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • Do functions declaration can be part of the lambda expression? – Eitanos30 Nov 11 '20 at 21:00
  • What will happen when i will write *onWebResponseHandler.aFunctionWhichIsDefinedAndNeverCalled*? also you said: *you can just pass a lambda to the constructor* but an interface doesn't have a constructor. – Eitanos30 Nov 11 '20 at 21:02
  • You are defining a _local function_, which can't be called from the outside. You can declare functions, as you have here, but they can only be called from within the lambda. – Louis Wasserman Nov 11 '20 at 21:07
  • Additionally, defining a `fun interface` _does_ create a constructor for the interface: one accepting a single lambda. – Louis Wasserman Nov 11 '20 at 21:08
  • i have never seen such a lambda.. usually lambda has input parameters followed by -> and then the implementation inside curly braces. I can't understand this lambda :( – Eitanos30 Nov 11 '20 at 21:13
  • [In Kotlin, a lambda can be anything surrounded by curly braces.](https://kotlinlang.org/docs/reference/lambdas.html#it-implicit-name-of-a-single-parameter) – Louis Wasserman Nov 11 '20 at 21:16
  • i know the link you have send me by by heart and still didn't find anything that is similar to the mess we wrote above. Can you tell please the function type of this horrible lambda expression? :) or a way the call the function from the lambda expression? – Eitanos30 Nov 11 '20 at 21:20
  • The lambda expression has the signature of your single abstract method of the functional interface. Lambdas with a single input parameter can omit the single parameter and it will implicitly be named `it`. That's what your code does. – Tenfour04 Nov 11 '20 at 21:21
  • What will happen if will write the following code:onWebResponseHandler.aFunctionWhichIsDefinedAndNeverCalled? – Eitanos30 Nov 11 '20 at 21:22
  • @Louis Wasserman, i will approve you answer because i'm sure you are right. But i will read it a lot to understand it. I'm very confused. Thanks! – Eitanos30 Nov 11 '20 at 21:33
1

Here's a different way of explaining it that might help.

If you want to assign an implementation of an interface to a variable or property, you usually have to use an anonymous class syntax like this. Note the use of object: and override.

val onWebResponseHandler = object : VolleyHandler.WebResponseHandler {
    override fun onWebResponseFinished(jsonObject: JSONObject) {
        Log.v("da", jsonObject.toString())
    }
}

You could also define other public functions in your anonymous class, but this is pointless because there is no way to access the unique members of an anonymous class, so you would not be able to call the function foo() in this example:

val onWebResponseHandler = object : VolleyHandler.WebResponseHandler {
    override fun onWebResponseFinished(jsonObject: JSONObject) {
        Log.v("da", jsonObject.toString())
    }

    fun foo() {
        Log.v("something", "something")
    }
}

If you make your interface a fun interface, that enables the use of lambda syntax to define that single function like this. This is what SAM conversion is. It treats your lambda as an anonymous implementation of your interface.

val onWebResponseHandler = VolleyHandler.WebResponseHandler { jsonObject ->
    Log.v("da", jsonObject.toString())
}

Furthermore, if a lambda has only one parameter, you can omit defining the parameter name, and simply call it it so this is equivalent to the above:

val onWebResponseHandler = VolleyHandler.WebResponseHandler {
    Log.v("da", it.toString())
}

Also, be aware that you can define a function anywhere in your code, including right in the middle of another function. For example:

fun bar() {
    
    fun foo() {
        Log.i("foo", "foo")
    }

    foo()
}

is equivalent to

fun bar() {
    
    val foo: () -> Unit = {
        Log.i("foo", "foo")
    }

    foo() // or foo.invoke()
}

So in your original code, you combined these concepts to create a lambda function that has other functions defined in it that are never used. The confusion probably comes because it looks similar to my top block of code, where we are defining an anonymous class with the object: syntax. The big difference is that you omitted object: and override, so you are using the lambda syntax. Your code is equivalent to:

val onWebResponseHandler = VolleyHandler.WebResponseHandler { jsonObject ->

    val onWebResponseFinished: (JSONObject) -> Unit = {
        Log.v("da", "dsa")
    }

    val foo: () -> Unit = {
        jsonObject.getString("name")
    }

}

which without lambda syntax (SAM conversion) would look like

val onWebResponseHandler = object : VolleyHandler.WebResponseHandler {
    override fun onWebResponseFinished(jsonObject: JSONObject) {
        val onWebResponseFinished: (JSONObject) -> Unit = {
            Log.v("da", "dsa")
        }

        val foo: () -> Unit = {
            jsonObject.getString("name")
        }
    }
}
Tenfour04
  • 83,111
  • 11
  • 94
  • 154
  • first of all thank you (like always). Secondly, can you tell me what is the role of the *object* in *object : VolleyHandler.WebResponseHandler*? Is it somehow related to companion? Because when i'm omitting the *object*i'm getting the follwoing error: *"Classifier 'WebResponseHandler' does not have a companion object, and thus must be initialized here"* – Eitanos30 Nov 11 '20 at 22:13
  • `object` is a way of defining a singleton class, which is a class with no constructor and that is automatically instantiated the first time it is used, and there only exists one instance for the whole app. There are three ways to use it. – Tenfour04 Nov 11 '20 at 22:20
  • 1
    (1) A companion object is one that goes along with some other class and can be called by that class's name. (2) You can simply define your whole class as an object by using `object` instead of `class`. (3) You can use them like I did above to create an anonymous class, which means you are creating an `object` that implements an interface or is a subclass of something without even giving it a name. Same concept as an anonymous class in Java. – Tenfour04 Nov 11 '20 at 22:22
  • SAM conversion is where you can use lambda syntax to create an anonymous implementation of an interface that has a Single Abstract Method instead of using the `object:` syntax. – Tenfour04 Nov 11 '20 at 22:24
  • thanks a lot, i think i understood **ALL** my mistakes – Eitanos30 Nov 12 '20 at 09:33