How to pass function with parameter jetpack compose - kotlin

i have a selaed class:
sealed class LessonEvent {
object OnEditBottomNavigationClick: LessonEvent()
object OnDeleteBottomNavigationClick: LessonEvent()
data class OnLessonClick(val lesson: Lesson): LessonEvent()
}
and another one
sealed class SchoolEvent {
data class OnSchoolClick(val school: School): SchoolEvent()
}
and want to pass this event to composable as parameter:
#Composable
fun TestItem(
onEvent: (parameter) -> Unit
)
so that onEvent can take SchoolEvent or LessonEvent as parameter and so i can access each object or data class
How can this be done in jetpack compose?

This question is not related to Jetpack Compose.
If you want to be able to pass both LessonEvent and SchoolEvent to the function, you need both of them to inherit the same interface, like:
sealed interface SomeEvent
sealed class LessonEvent: SomeEvent {
object OnEditBottomNavigationClick: LessonEvent()
object OnDeleteBottomNavigationClick: LessonEvent()
data class OnLessonClick(val lesson: Lesson): LessonEvent()
}
sealed class SchoolEvent: SomeEvent {
data class OnSchoolClick(val school: School): SchoolEvent()
}
And then:
#Composable
fun TestItem(event: SomeEvent,
onEvent: (SomeEvent) -> Unit
)

Related

Override generic method in abstract class with concrete type in subclass

I have an main class in Kotlin which defines one concrete and one abstract generic method as follows:
abstract class MainClass {
abstract fun <TParent, TChild> getChildren(parent: TParent): Array<TChild>?
fun <TParent, TChild> processChildren(parent: TParent) {
val children = getChildren<TParent, TChild>(parent)
// ... do something with children
}
}
As you can see the method is about a parent object of type TParent containing child objects of type TChild. The parent class and how to get the children from it should be defined by subclasses, which should provide the concrete types.
I did the following:
class MyClass : MainClass{
override fun getChildren(parent: MyParent): Array<MyChild>? {
//... some logic getting the children from a parent object
}
}
But that does not work because it won't compile ('getChildren' overrides nothing).
How can I define a generic abstract method and implement it with concrete types in a subclass? Note that it is important to me that the generic types are defined on method-level, not on class level!
You cannot do this. Due to the type erasure the methods signatures will not fit.
What you actually can do is to make this abstract method protected and overload it with some specific types
// protected abstract fun <TParent, TChild> getChildren...
class MyClass: MainClass() {
override fun <TParent, TChild> getChildren(parent: TParent): ArrayList<MyChild>? {
// Some dummy implementation
println(parent!!::class.java.canonicalName)
return null
}
fun getChildren(parent: MyParent): ArrayList<MyChild>? {
return getChildren<MyParent, MyChild>(parent)
}
fun getChildren(parent: MyParent2): ArrayList<MyChild2>? {
return getChildren<MyParent2, MyChild2>(parent)
}
}
But not really sure what is the sense of doing this especially if generic types are not bounded
Looks like you don't want the method in one class to process different types of parameters.
In this scenario, make the class generic:
abstract <TParent, TChild> class MainClass {
abstract fun getChildren(parent: TParent): Array<TChild>?
fun processChildren(parent: TParent) {
val children = getChildren<TParent, TChild>(parent)
// ... do something with children
}
}
class MyClass : MainClass<MyParent, MyChild>() {
override fun getChildren(parent: MyParent): Array<MyChild>? {
//... some logic getting the children from a parent object
}
}

How to call an abstract method from a Class parameter in Kotlin?

Aim
Have a function Book, which takes one of three Letter classes as argument myClass and then calls 'genericMethod()' from the abstract class which Letter*() has inherited.
Issue
If I try Book(LetterA()).read() I get the following error:
Type mismatch. Required: Class<SampleClassArguments.Alphabet> Found: SampleClassArguments.LetterA
Does Kotlin have any way to achieve this result?
Code
#Test
fun readBookTest() {
Book(LetterA()).read() /*<--error here*/
}
class Book(val myClass: Class<Alphabet>) {
fun read() {
val letterClass = myClass.getConstructor().newInstance()
letterClass.genericMethod(myClass.name)
}
}
class LetterA(): Alphabet()
class LetterB(): Alphabet()
class LetterC(): Alphabet()
abstract class Alphabet {
fun genericMethod(className: String) {
println("The class is: $className")
}
}
You need to define the Class type as covariant with the out keyword so any of the child classes is an acceptable argument:
class Book(val myClass: Class<out Alphabet>)
And when you use it, you need to pass the actual Class, not an instance of the class. You can get the Class by calling ::class.java on the name of the class:
#Test
fun readBookTest() {
Book(LetterA::class.java).read()
}

Moshi PolymorphicJsonAdapter for nested sealed classes maybe not generating adapter correctly

When a subclass of a sealed class has a property that is a subclass of a different sealed class the generated adapter does not recognize the correct types. For example:
Given the following classes:
sealed class Operation(
val id: Long,
val params: Params?,
) {
sealed class Params()
}
sealed class SpecialOperation(
id: Long,
params: SpecialOperation.Params?,
) : Operation(id, params) {
class Params : Operation.Params()
}
The generated adapter for the SpecialOperation class constructs an instance of Operation.Params instead of an instance of SpecialOperation.Params which then throws an exception: Type mismatch: inferred type is Operation.Params? but SpecialOperation.Params? was expected.
I was able to work around this behavior by making the properties from the super sealed class Operation open and overriding them in the subclass SpecialOperation like the following:
sealed class Operation(
open val id: Long,
open val params: Params?,
) {
...
}
sealed class SpecialOperation(
override val id: Long,
override val params: SpecialOperation.Params?,
) : Operation(id, params) {
...
}
In this situation shouldn't the generated adapter check the type of the constructor argument instead of the type of the property on the superclass as one is a subtype of the other?

Calling an overloaded method with the base class parameter type

Is it possible in Kotlin to call an overloaded method using the base class type as a parameter? This is best illustrated via an example
Base Sealed Class + Derived Classes
sealed class Event {
abstract val eventId: String
}
data class FirstEvent(
override val eventId: String
val first: String
) : Event()
data class SecondEvent(
override val eventId: String
val second: String
) : Event()
Utility Class having an overloaded method for each of the derived classes
class UtilityClass {
fun handle(event: FirstEvent) {
....
}
fun handle(event: SecondEvent) {
....
}
}
Is it possible to call methods of the utility class in such a way utility.handle(FirstEvent("id", "first) as Event) doing so is giving me the following exception
None of the following functions can be called with the arguments supplied.
you can do something like this
fun handleEvent(event: Event) {
when (event) {
is FirstEvent -> {
// event is automatically casted as FirstEvent
event.first
}
is SecondEvent -> ...
}
}

Using android-extensions`s Parcelize annotation on objects that extends Parceable sealed classes

I'm using kotlin android extensions to auto generate my Parcelables, but given the following code I'm getting 'Parcelable' should be a class.
the code:
sealed class Action : Parcelable
#Parcelize
object Run : Action()
#Parcelize
data class Ask(
val question: String
) : Action()
My understanding is that it is impossible to use #Parcelize in an object (Once it is working on the Ask class) in the way I'm doing it.
I want to use the Parcelable annotation in an object that extends a sealed class, so I'm do not know how to do it and do not write the following boilerplate.
object Run : Action() {
override fun writeToParcel(p0: Parcel?, p1: Int) {}
override fun describeContents() = 0
#JvmField
val CREATOR = object : Parcelable.Creator<Run> {
override fun createFromParcel(parcel: Parcel) = Run
override fun newArray(size: Int) = arrayOfNulls<Run?>(size)
}
}
You need to make sure that you have you Kotlin up to date.
You can follow an example here.