1

I have my custom GetVolley Class

class GetVolley(private val mContext: Context, private val url: String, private val onVolleySuccess: OnVolleySuccess?, private val mOnVolleyError: OnVolleyError?, private val mOnVolleyEnd: OnVolleyEnd?) {
    private fun getDataVolley() {
        Log.d("GetVolleyUrl", url)
        val request = JsonObjectRequest(Request.Method.GET, url, null, Response.Listener { response: JSONObject ->
            Log.d("GetVolleyResult", response.toString())
            if (response.has("result") && response.getBoolean("result")) {
                onVolleySuccess?.onSuccess(response)
            } else if (response.has("message")) {
                if (mOnVolleyError != null) mOnVolleyError.onError(response.getString("message"))
                else Toast.makeText(mContext, response.getString("message"), Toast.LENGTH_LONG).show()
            }
            mOnVolleyEnd?.onEnd()
        }, Response.ErrorListener { volleyError: VolleyError ->
            Log.d("GetVolleyError", volleyError.message.toString())
            val errorMessage: String = if (volleyError is NetworkError || volleyError is AuthFailureError) {
                "No Internet…"
            } else {
                "Undefinded error"
            }
            if (mOnVolleyError != null) mOnVolleyError.onError(errorMessage)
            else Toast.makeText(mContext, errorMessage, Toast.LENGTH_LONG).show()
            mOnVolleyEnd?.onEnd()
        })
        Volley.newRequestQueue(mContext).add(request)
    }

    init {
        getDataVolley()
    }
}

Interfaces in different files:

interface OnVolleySuccess {
    fun onSuccess(response: JSONObject)
}
______________________________________
interface OnVolleyError {
    fun onError(error: String)
}
______________________________________
interface OnVolleyEnd {
    fun onEnd()
}

When I use my custom GetVolley request. My code look like:

GetVolley(this, url, object : OnVolleySuccess {
    override fun onSuccess(response: JSONObject) {
        parseResponse(response)
    }
}, object : OnVolleyError {
    override fun onError(error: String) {
        showError(error)
    }
}, null)

I want it to look like this:

GetVolley(this, url, response -> {
    parseResponse(response)
}, error -> {
    showError(error)
}, null)

All my callbacks is nullable, so I can set null onVolleySuccess, onVolleyError, onVolleyEnd.

2 Answers2

2

What you what is called SAM conversions (Single Abstract Method). It's only available to Java interfaces, but will be available to Kotlin interface from Kotlin 1.4. (see this article). Now if you want lambdas, use lambdas:

class GetVolley(
    private val mContext: Context, 
    private val url: String, 
    private val onVolleySuccess: ((response: JSONObject) -> Unit)?, 
    private val mOnVolleyError: ((error: String) -> Unit)?, 
    private val mOnVolleyEnd: (() -> Unit)?
) {

And instead of doing:

onVolleySuccess?.onSuccess(response)

You do:

onVolleySuccess?.invoke(response)
Nicolas
  • 6,611
  • 3
  • 29
  • 73
  • version 1.4 is not released yet, so can I use this version in production? or maybe i should wait it for release? – Shuhratjon Jumaev May 30 '20 at 13:54
  • You can use it (see @AnimeshSahu answer), but I strongly encourage you to use lambdas instead, it's more idiomatic. Also 1.4 isn't stable yet. – Nicolas May 30 '20 at 13:56
  • @ShuhratjonJumaev EAP are not ready for production https://kotlinlang.org/eap/ `By participating in the EAP, you expressly acknowledge that the EAP version may not be reliable, may not work as intended, and may contain errors.` – Animesh Sahu May 30 '20 at 14:03
  • @Nicolas I used your solution. And it worked without addtional interfaces, and my code looks better which I liked. It’s so not important, by the way my code look like this now: ``` GetVolley(this, url, {//it: JSONObject parseResponse(it) }, {//it: JSONObject showError(it) }, null) ``` Can that "it" be replaced with "response"? – Shuhratjon Jumaev May 30 '20 at 14:15
  • Yes, `it` is just the default name. You can write `{ response ->` to name it. – Nicolas May 30 '20 at 14:17
  • @Nicolas thank you! This is what i absolutly needed. PS... Sorry for that unformatted code in my comment. I could not format that))). – Shuhratjon Jumaev May 30 '20 at 14:20
1

Note: Use Nicolas solution if you want, as it uses lambda natively if you don't want to use these classes in Java

SAM conversion, i.e. interfaces to callbacks conversion using lambda is only available since Kotlin 1.4, which is in EAP (Early Access Program).

If you have Kotlin 1.4-M1 which is currently being released a few months ago, you can declare your interface with a fun keyword which will make it available to be used as lambda.

fun interface OnVolleySuccess {
    fun onSuccess(response: JSONObject)
}
fun interface OnVolleyError {
    fun onError(error: String)
}
fun interface OnVolleyEnd {
    fun onEnd()
}

Now you can use it like this:

GetVolley(this, url, { response ->
    parseResponse(response)
}, { error ->
    showError(error)
}, null)

// or simply
GetVolley(this, url, { parseResponse(it) }, { showError(it) }, null)

If you are having difficulties in adopting Kotlin 1.4, here's the catch:

// add/change these in build.gradle:
plugins {
    id 'org.jetbrains.kotlin.jvm' version '1.4-M1'
    // ...
}

repositories {
    // ...
    maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
}

// add these in settings.gradle
pluginManagement {
    repositories {
        mavenCentral()
        maven { "https://dl.bintray.com/kotlin/kotlin-eap" }
        maven { "https://plugins.gradle.org/m2/" }
    }
}
Animesh Sahu
  • 7,445
  • 2
  • 21
  • 49