API design question! I'm writing a API/library in ...
# announcements
s
API design question! I'm writing a API/library in Kotlin. For a method if it returns a sealed class object then is there anything I can do to make it nicer when the API is used from Java side ? For example
Copy code
sealed class Response {
    object Success : Response()
    data class Failure(val error: String) : Response()
}

object Test {
    @JvmStatic
    fun returnSealed(): Response {
        return Response.Success
    }
}
On Java side, caller would have to use
instanceOf
if-else logic. Is there a way to make it better for them ?
Copy code
Response response = Test.returnSealed();
if (response instanceof Response.Failure) {
    ((Response.Failure) response).getError();
} else {
    // handle success
}
r
You could add an
onSuccessOrElse(() -> blah, err -> blah)
method to
Response
Probably accompanied by
onSuccess
and
onError
s
is this a known pattern on Java side ? Can you share any example ? I want to make the API feel more natural for Java users.
r
It would be similar to what Optional does
s
Thanks. I'll give it a try.
👀 1
r
np
s
aah so that would be restrictive in a sense that I would be defining a strict method for API user especially that lambda signature. If they want to return their own object then they can't use it.
r
Add a method that takes a
Supplier<T>
and a
Function<String, T>
then
s
Yeah so something like this based on what you suggested ?
Copy code
sealed class Response {
    abstract fun <T> onSuccessOrElse(onSuccess: () -> T, onError: (String) -> T)

    object Success : Response() {
        override fun <T> onSuccessOrElse(onSuccess: () -> T, onError: (String) -> T) {
            onSuccess()
        }
    }

    data class Failure(val error: String) : Response() {
        override fun <T> onSuccessOrElse(onSuccess: () -> T, onError: (String) -> T) {
            onError(error)
        }
    }
}
r
Yeah that would work
Though it won't accept void-producing lambdas
s
yeah I realized that. Is there a way to solve it ? I think that's usually the problem I have seen where I have to explicitly return
Unit.INSTANCE
from Java
r
I don't know if overloading works properly (test it), but if it doesn't you could just write another function taking a nullary void functional interface and a one-ary void functional interface
s
With functional interface, final version would look like this
Copy code
fun interface SuccessHandler {
    fun invoke()
}

fun interface FailureHandler {
    fun invoke(error: String)
}

sealed class Response {
    abstract fun <T> onSuccessOrElse(onSuccess: SuccessHandler, onError: FailureHandler)

    object Success : Response() {
        override fun <T> onSuccessOrElse(onSuccess: SuccessHandler, onError: FailureHandler) {
            onSuccess.invoke()
        }
    }

    data class Failure(val error: String) : Response() {
        override fun <T> onSuccessOrElse(onSuccess: SuccessHandler, onError: FailureHandler) {
            onError.invoke(error)
        }
    }
}
calling code from Java would like this
Copy code
response.onSuccessOrElse(() -> {
            // Handle success
        }, error -> {
            // Handle failure with error
        });
I think this is a good solution to my problem. Thanks @randomcat
r
np
s
bway @randomcat Thanks for the help. I wrote a blog post about it and thanked you there as well. I hope I found your correct github profile https://jabbar.hashnode.dev/kotlin-sealed-class-api-for-java-consumption
r
cool, and you did
👍 1
although, technically SuccessHandler and FailureHandler's
invoke
methods do return Unit in Kotlin, they just get generated to return
void
in Java because the return type is not generic
s
Yeah looks like I didn't do good job at clarifying that somewhere?
r
Since our
invoke
method in the interface doesn't return
Unit
like our
Lambda
, our Java code doesn't need to return anything anymore.
this could be more explicit, but I'm just a pedant
good post overall
s
aah makes sense. I'll 100% update it tonight. It's better to use correct words and add more details so someone somewhere doesn't take the incorrect thing literally and get incorrect knowledge. Thank you for catching that.
r
np