Pass interface as parameter in Kotlin - android

I want to pass an interface as parameter like this:
class Test {
fun main() {
test({})
// how can I pass here?
}
fun test(handler: Handler) {
// do something
}
interface Handler {
fun onCompleted()
}
}
In Java, I can use anonymous function like test(new Handler() { .......... }), but I can't do this in Kotlin. Anyone know how to do this?

In Kotlin you can do :
test(object: Handler {
override fun onComplete() {
}
})
Or make a property the same way:
val handler = object: Handler {
override fun onComplete() {
}
}
And, somewhere in code:
test(handler)

since your interface has only one function. you can convert it to SAM like this
fun interface Handler {
fun onCompleted()
}
then you can just implement this interface using lambda instead and so reduce the overall written code. this is only possible in v1.4

Attached is an example of how to pass an object by parameter that represents the value of the data type and invoke behaviors using interface inheritance.
fun main() {
val customClass = CustomClass(
object : First {
override fun first() {
super.first()
println("first new impl")
}
override fun second() {
super.second()
println("second new impl")
}
}
)
customClass.first.first()
customClass.first.second()
}
data class CustomClass(val first: First)
interface First: Second {
fun first() {
println("first default impl")
}
}
interface Second {
fun second() {
println("second default impl")
}
}
It is worth mentioning that with super.first() or super.second() the default behavior of the interface is being invoked.
It doesn't make much sense to pass a lamda with an anonymous object as a parameter, lambda: () -> Unit , if what we need is to invoke the functions.
GL
Playground

Related

How to convert a object expression to lambda in Kotlin? [duplicate]

This question already has answers here:
Passing lambda instead of interface
(5 answers)
Closed 1 year ago.
I am new to Android development. Recently I am learning Kotlin and I am trying to figure out setOnClickListener. However, I encountered a problem in the process of converting object expression to lambda using Kotlin.
step 1. setOnClickListener in Java:
buttonLogin.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// my code
}
});
step 2. then I convert Java code to Kotlin code using object expression:
buttonLogin.setOnClickListener(object : View.OnClickListener {
override fun onClick(p0: View?) {
// my code
}
})
step 3. then IntelliJ prompts me to convert object expression to lambda:
buttonLogin.setOnClickListener {
// my code
}
It looks more concise, however, I can't understand the logic behind step 3.
So I checked some information online, it says
Any function that receives an interface with a single function can be
substituted by a lambda
It does on setOnClickListener.
But I still can't fully understand, so I defined an interface and class to verify it.
Here is my code:
interface MyInterface {
fun method1()
}
class MyClass {
fun method2(myInterface: MyInterface) {
myInterface.method1()
}
}
fun main() {
val myClass = MyClass()
myClass.method2(object : MyInterface {
override fun method1() {
println("Hello, world.")
}
})
// So how to write the lambda to replace object expression?
}
The code in step 3 is called trailing lambdas
According to Kotlin convention, if the last parameter of a function is a function, then a lambda expression passed as the corresponding argument can be placed outside the parentheses,
If the lambda is the only argument in that call, the parentheses can be omitted entirely:
For example:
fun function(f : (String) -> Unit) {
}
fun main() {
function {
}
}
Check the Kotlin Documentation for this feature
https://kotlinlang.org/docs/lambdas.html#passing-trailing-lambdas
You can convert your code to use this feature
class MyClass {
fun method2(function: () -> Unit) {
function()
}
}
fun main() {
val myClass = MyClass()
myClass.method2 {
println("Hello, world.")
}
// Or you can store the lambda in variable and use it like tihs
val myClass2 = MyClass()
val function = {
println("Hello, world.")
}
myClass2.method2(function)
}
Or just add convert your interface to a functional interface
fun interface MyInterface {
fun method1()
}
class MyClass {
fun method2(myInterface: MyInterface) {
myInterface.method1()
}
}
fun main() {
val myClass = MyClass()
myClass.method2 {
println("Hello, world.")
}
}

Kotlin Flow: callbackFlow with lazy initializer of callback object

I want to use reactive paradigm using Kotlin Flow in my Android project. I have an external callback-based API so my choice is using callbackFlow in my Repository class.
I've already read insightfully some proper docs with no help:
callbackFlow documentation
Callbacks and Kotlin Flows by Roman Elizarov
What I want to achieve:
Currently my Repository class looks like this (simplified code):
lateinit var callback: ApiCallback
fun someFlow() = callbackFlow<SomeModel> {
callback = object : ApiCallback {
override fun someApiMethod() {
offer(SomeModel())
}
}
awaitClose { Log.d("Suspending flow until methods aren't invoked") }
}
suspend fun someUnfortunateCallbackDependentCall() {
externalApiClient.externalMethod(callback)
}
Problem occurs when someUnfortunateCallbackDependentCall is invoked faster than collecting someFlow().
For now to avoid UninitializedPropertyAccessException I added some delays in my coroutines before invoking someUnfortunateCallbackDependentCall but it is kind of hack/code smell for me.
My first idea was to use by lazy instead of lateinit var as this is what I want - lazy initialization of callback object. However, I couldn't manage to code it altogether. I want to emit/offer/send some data from someApiMethod to make a data flow but going outside of callbackFlow would require ProducerScope that is in it. And on the other hand, someUnfortunateCallbackDependentCall is not Kotlin Flow-based at all (could be suspended using Coroutines API at best).
Is it possible to do? Maybe using some others Kotlin delegates? Any help would be appreciated.
To answer your question technically, you can of course intialise a callback lazyily or with lateinit, but you can't do this AND share the coroutine scope (one for the Flow and one for the suspend function) at the same time - you need to build some kind of synchronisation yourself.
Below I've made some assumptions about what you are trying to achieve, perhaps they are not perfect for you, but hopefully give some incite into how to improve.
Since it is a Repository that you are creating, I will first assume that you are looking to store SomeModel and allow the rest of your app to observe changes to it. If so, the easiest way to do this is with a MutableStateFlow property instead of a callbackFlow:
interface Repository {
val state: Flow<SomeModel>
suspend fun reload()
}
class RepositoryImpl(private val service: ApiService) : Repository {
override val state = MutableStateFlow(SomeModel())
override suspend fun reload() {
return suspendCoroutine { continuation ->
service.callBackend(object : ApiCallback {
override fun someApiMethod(data: SomeModel) {
state.value = data
if (continuation.context.isActive)
continuation.resume(Unit)
}
})
}
}
}
interface ApiCallback {
fun someApiMethod(data: SomeModel)
}
data class SomeModel(val data: String = "")
interface ApiService {
fun callBackend(callback: ApiCallback)
}
The downside to this solution is that you have to call reload() in order to actually make a call to your backend, collecting the Flow alone is not enough.
myrepository.state.collect {}
myrepository.reload()
Another solution, again depending on what exactly you are trying to achieve, is to provide two ways to call your backend:
interface Repository {
fun someFlow(): Flow<SomeModel>
suspend fun reload(): SomeModel
}
class RepositoryImpl(private val service: ApiService) : Repository {
override fun someFlow() = callbackFlow<SomeModel> {
service.callBackend(object : ApiCallback {
override fun someApiMethod(data: SomeModel) {
offer(data)
}
})
awaitClose {
Log.d("TAG", "Callback Flow is closed")
}
}
override suspend fun reload(): SomeModel {
return suspendCoroutine<SomeModel> { continuation ->
service.callBackend(object : ApiCallback {
override fun someApiMethod(data: SomeModel) {
if (continuation.context.isActive)
continuation.resume(data)
}
})
}
}
}
interface ApiCallback {
fun someApiMethod(data: SomeModel)
}
data class SomeModel(val data: String = "")
interface ApiService {
fun callBackend(callback: ApiCallback)
}
Now you can either call reload() or someFlow() to retrieve SomeModel() and the Repository holds no "state".
Note that the reload() function is simply a 'coroutine' version of the callbackFlow idea.

What is the best way to set my delegate variable (inner class vs. this)?

So, I have a following BLEClient class and I'm currently setting deviceDelegate using this keyword
class BLEClient(val device: Device) : Client, DeviceDelegate {
init {
device.deviceDelegate = this
}
// client interface methods
override fun connect() {...}
override fun disconnect() {...}
override fun send() {...}
// device delegate interface methods
override fun didSend() { ... }
override fun didConnect() { ... }
override fun didReceive() { ... }
}
I was wondering if this is the best way to do it, since I could also use an inner class for DeviceDelegate instead of implementing interface directly, in my eyes this would make the code more readable and simple?
The class would look like this:
class BLEClient(val device: Device) : Client {
init {
device.deviceDelegate = DeviceDelegateInner()
}
/* client interface methods */
override fun connect() {...}
override fun disconnect() {...}
override fun send() {...}
inner class DeviceDelegateInner() : DeviceDelegate {
override fun didSend() { }
override fun didConnect() { }
override fun didReceive() { }
}
}
Are there any significant drawbacks if I set my delegate using an inner class instead of implementing an interface directly and setting it using this keyword?
What would you guys prefer? Which way is better?
It really depends on your use-case, and I'm not familiar with DeviceDelegate at all. But I'll give it a try.
Using inner class provides better encapsulation and separation of concerns.
Also, implementing less interfaces makes your class a bit easier to reason about, for the same reasons above.
You can also argue that the second approach is more "composition over inheritance".
I would use the first approach only if you see a lot of duplication in your inner class, eg:
class BLEClient(val device: Device) : Client {
fun b() { }
inner class DeviceDelegateInner() : DeviceDelegate {
override fun a() = b()
}
}

Kotlin syntax for LiveData observer?

I have the following bit of code in my HomeActivity to use LiveData.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Show the launch splash screen.
//
this.setContentView(R.layout.activity_home)
this.viewModel = ViewModelProviders.of(this).get(HomeViewModel::class.java)
this.viewModel.getUser().observe(this, Observer { user: User? ->
});
}
While this seems to work, what does the following part mean?
Observer { user: User? ->
}
This must result in an object that conforms to the Observer interface which has
void onChanged (T t)
https://developer.android.com/reference/android/arch/lifecycle/Observer.html
How does
Observer { user: User? ->
}
result in an object with an onChanged method?
I don't know what putting the name of an interface in front of a lambda expression means.
Thanks!
This is called SAM Conversion, a concept that helps interacting with Java Single Abstract Method Interfaces like in your example.
The following creates an implementation of Runnable, where the single abstract method is run():
val runnable = Runnable { println("This runs in a runnable") }
It’s described in the docs: https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
Alternatively, but more verbose, would be to use an object:
val runnable2 = object : Runnable {
override fun run() {
println("This runs in a runnable")
}
}
Both are examples of anonymous implementations of that interface. It's of course also possible to create a concrete subclass and instantiate it then.
class MyRunnable : Runnable {
override fun run() {
println("This runs in a runnable")
}
}
val runnable3 = MyRunnable()
in Kotlin the Observer { } lambda gives you param it, you can rename it as you want and use. by default data will be available with it.something() etc...
JAVA:
... new Observer {
void onChanged(User user){
user.something()
}
}
KOTLIN
... object : Observer<User> {
fun onChanged(user: User){
user.something()
}
}
OR
... Observer {
it.something()
}
you can rename it to whatever you want like
... Observer { myUser ->
myUser.something()
}
To omit the Observer { ... } part just add import androidx.lifecycle.observe and use it like this:
this.viewModel.user.observe(this) { user: User? ->
// ...
}

Android Kotlin Mvp Class delegation

So I have the following scenario:
class NowActivity: AppCompatActivity(), NowScreen, NowDelegate by NowDelegateImpl(){
onCreate(...){
presenter.attachView(this)
}
Is there any way I can delegate the implementation of some NowScreen methods to NowDelegate so I can do the following inside the presenter:
view.callSomeFunc()
in which callSomeFund() is implemented in NowDelegate.
Is there any way of accomplish something like this? the problem is that I'm using MVP, which attach a view to a presenter. But some of the view implementation is repeated in several activities, so I would want to delegate it to another class.
You can delegate both interfaces to the same object if it implements both interfaces. To do so just make the object a constructor parameter, for example:
class NowActivity(delegate: NowDelegateImpl): AppCompatActivity(),
NowScreen by delegate,
NowDelegate by delegate {
constructor (): this(NowDelegateImpl()) {} // need this default constructor for Android to call
...
}
If the delegate does not implement everything of both interfaces, you can make it a member and manually delegate some subset of the functions to it.
class NowActivity(private val delegate: NowDelegateImpl):
AppCompatActivity(),
NowScreen,
NowDelegate by delegate {
constructor (): this(NowDelegateImpl()) {} // need this default constructor for Android to call
override fun callSomeFund() { delegate.callSomeFund() }
}
Both options need you to create a default constructor that creates the object used for delegation and passes that to the primary constructor.
Here it is broken out to an all inclusive sample that isn't so Android specific in case others want to see all that is going on...
Example 1, delegate all interfaces to same object:
interface CommonStuff {
fun foo1()
fun foo2()
}
interface LessCommonStuff {
fun bar()
}
class CommonDelegate1: CommonStuff, LessCommonStuff {
override fun foo1() {}
override fun foo2() {}
override fun bar() {}
}
class Activity1(delegate: CommonDelegate1):
LessCommonStuff by delegate,
CommonStuff by delegate {
constructor (): this(CommonDelegate1()) {} // need this default constructor
// ...
}
Example 2, manually delegate some interfaces using a member:
interface CommonStuff {
fun foo1()
fun foo2()
}
interface LessCommonStuff {
fun bar()
}
class CommonDelegate2: CommonStuff {
override fun foo1() {}
override fun foo2() {}
fun barLikeThing() {}
}
class Activity2(private val delegate: CommonDelegate2):
LessCommonStuff,
CommonStuff by delegate {
constructor (): this(CommonDelegate2()) {} // need this default constructor
override fun bar() { delegate.barLikeThing() }
}

Categories

Resources