Sealed classes generics - kotlin

I have this scenario where I have a super abstract class that emits different types of events using Kotlin sealed classes.
These events are modeled as follows.
sealed class BaseEvent {
object ConnectionStarted : BaseEvent()
object ConnectionStopped : BaseEvent()
}
sealed class LegacyEvent : BaseEvent() {
object TextChanged : LegacyEvent()
object TextCleared : LegacyEvent()
}
sealed class AdvancedEvent : BaseEvent() {
object ButtonClick : AdvancedEvent()
object ButtonLongClick : AdvancedEvent()
}
And here are the classes that emit these events
abstract class BaseViewModel<E : BaseEvent> {
private fun startConnection() {
emit(BaseEvent.ConnectionStarted) // <-- Error
}
fun emit(event: E){
//...
}
}
class LegacyBaskan : BaseViewModel<LegacyEvent>() {
fun textChanged() {
emit(LegacyEvent.TextChanged) // <-- Works
}
}
class AdvancedBaskan : BaseViewModel<AdvancedEvent>() {
fun buttonClicked() {
emit(AdvancedEvent.ButtonClick) // <-- Works
}
}
Here, it only works for the subclass and I can emit any event in the LegacyEvent or AdvancedEvent in their associated classes. However, for the BaseBaskan class, I can't emit the events from the BaseEvent although I stated that the generic type E must extend the BaseEvent.
I need each subclass to have access to its own events as well as the superclass events, but not the other subclasses' events.
How can I still emit events from BaseEvent in the base class, while giving each class the access to emit its own events only?

Not sure if you're confused about why it's not letting you emit the item from the base class. Since E could be any subtype of BaseEvent, if your class could emit ConnectionStarted, then it would be violating its contract any time it is declared as a BaseViewModel<AnythingBesidesConnectionStarted>.
Only way I can think of to make this work is have both private and public versions of the emit function. You might have to change code elsewhere in your class that you haven't shown. If there's some function that returns E, you will have to change it so it returns BaseEvent.
abstract class BaseViewModel<E : BaseEvent> {
private fun startConnection() {
emitInternal(BaseEvent.ConnectionStarted)
}
private fun emitInternal(event: BaseEvent) {
//...
}
fun emit(event: E){
emitInternal(event)
}
}

You can't emit BaseEvent.ConnectionStarted in BaseViewModel (and other events as well) because E is not defined yet, so the type system can't be sure that you won't emit events of another subtype breaking generic type invariance.
Just add an overloaded private version, which accepts BaseEvent argument (you'll need some #JvmName annotation to make it compilable for JVM target):
abstract class BaseViewModel<E : BaseEvent> {
private fun startConnection() {
emit(BaseEvent.ConnectionStarted)
}
#JvmName("emitBaseEvent")
private fun emit(event: BaseEvent) {
//...
}
fun emit(event: E) {
emit(event as BaseEvent)
}
}

It looks like you need contravariance, which can be achieved using in. Assuming your base class only has methods such as emit that use type E as parameter type, not as return type, then:
abstract class BaseViewModel<in E : BaseEvent> {
See https://kotlinlang.org/docs/generics.html#use-site-variance-type-projections.

Related

Overriding method in separate instantiated class

There are 2 classes inherited from parent:
abstract class Parent{
abstract fun prepare()
abstract fun act()
}
class Child1:Parent(){
fun prepare()
fun act()
}
class Child2:Parent(){
fun prepare()
fun act()
}
I need to modify only method act() just to perform some action before it and run it after:
class Modify (parent:Parent):parent{
override fun act(){
...//modification
parent.act() //from Child1 or Child2 depends
}
}
class Modify1 (parent:Parent):parent{}
class Modify2 (parent:Parent):parent{}
The idea is to have several modificators and use them arbitrarily both for modified and not modified instances along with using several serial modificators:
val modifiedChild1:Parent = Modify1 ( Modify3( Child1() ) )
val modifiedChild2:Parent = Modify1 ( Child2() )
val notModified1:Parent = Child1 ()
Please advise if this concept is correct and how to implement it. Thanks.
the only way I found: is to add an interface as listener.
But in that case, it is not possible to use Modify() as a function and possibility perform several modifications.
If you're interested in modifying the public functions of Parent, you can use the Decorator pattern to create a wrapper class and override the functions. For example:
// Copyright 2023 Google LLC.
// SPDX-License-Identifier: Apache-2.0
abstract class Parent {
abstract fun prepare()
abstract fun act()
}
class ParentDecorator(val parent: Parent): Parent() {
override fun prepare() {
// modification
parent.prepare()
// modification
}
override fun act() {
// modification
parent.act()
// modification
}
}
class Child: Parent() { ... }
val child = Child()
val decoratedChild = ParentDecorator(child)
decoratedChild.prepare()
// will call the modifications around the call to the
// nested child instance
This only works if the parent is abstract, open, or is an interface, and only affects the public functions that you override in the Decorator.
Note - this example is a tad awkward - we're creating an instance of Parent to override all of its functions and wrap another instance of Parent, delegating the real function to that wrapped instance. It would be much better to make Parent an interface if you want to create a Decorator. For example:
// Copyright 2023 Google LLC.
// SPDX-License-Identifier: Apache-2.0
interface Stuff {
fun prepare()
fun act()
}
class StuffDecorator(val realStuff: Stuff): Stuff {
override fun prepare() {
// modification
realStuff.prepare()
// modification
}
override fun act() {
// modification
realStuff.act()
// modification
}
}
class Child: Stuff { ... }
// could extend another class that implements Stuff
val child = Child()
val decoratedChild = StuffDecorator(child)
decoratedChild.prepare()
// will call the modifications around the call to the
// nested child instance
However, if you're stuck with an existing open/abstract Parent class, this is the best you can do)
If you want to do this is non-public functions, #cyberbrain's mention of AspectJ is more the way to go (but I'm not sure that can work with Kotlin - I see some mentions, such as https://github.com/serpro69/kotlin-aspectj-maven-example/blob/master/README.md, but no idea if that actually works).

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
}
}

Kotlin type inference on "supposedly" right types

I am new to Kotlin and I was playing with it. I pretty much wanted to create a pretty basic event bus. So I came up with this
interface Event
interface EventListener<E : Event> {
fun handle(event: E)
}
interface EventBus {
fun <E : Event> registerListener(aClass: Class<E>, eventListener: EventListener<E>)
}
class MyBus() : EventBus {
private val eventListeners: MutableMap<String, MutableList<EventListener<out Event>>> = mutableMapOf()
constructor(listeners: List<Pair<Class<Event>, EventListener<Event>>>) : this() {
listeners.forEach {
registerListener(it.first, it.second)
}
}
override fun <E : Event> registerListener(aClass: Class<E>, eventListener: EventListener<E>) {
val key = aClass.name
val listeners: MutableList<EventListener<out Event>> = eventListeners.getOrPut(key) { mutableListOf() }
listeners.add(eventListener)
}
}
val bus = MyBus(
listOf(
MyEvent::class.java to MyEventListener()
)
)
class MyEvent : Event
class AnotherEvent : Event
class MyEventListener : EventListener<MyEvent> {
override fun handle(event: MyEvent) {
}
}
what happens is that when I try to create MyBus using the constructor accepting the list of pairs, I get
Type inference failed. Expected type mismatch: inferred type is List<Pair<Class<MyEvent>,MyEventListener>> but List<Pair<Class<Event>,EventListener<Event>>> was expected
But if I change the constructor to be something like
constructor(listeners: List<Pair<Class<out Event>, EventListener<out Event>>>) : this() {
listeners.forEach {
registerListener(it.first, it.second)
}
}
adding out pretty much everywhere, then the MyBus constructor works, but the invocation to registerListener(..) breaks for the same exact reason as before. So the only way to solve this is to add "out"s also on registerListener function.
I suspect I'm doing something wrong here, but I don't know what precisely. Any help?
If you want your EventListener to be able to consume Events, then its type has to be invariant or covariant (not declared out). If it let you pass your EventListener<MyEvent> as if it were an EventListener<Event>, then your MyBus class might call listener.handle(event) on it with some Event that is not a MyEvent, such as AnotherEvent. Then you will get a ClassCastException when it tries to cast this AnotherEvent to MyEvent.
To be able to store different types of invariant EventHandlers, you will have to remove the variance restrictions by using star projection, and cast them when you retrieve them from the map. So make the map keys into class objects instead of just Strings. Since you will not have the help of the compiler when working with the star-projected types, you need to be careful that you are only adding an item to your MutableMap that is of the same type as the Class key that's associated with it. Then when you retrieve items, only cast to an invariant type.
The other part of your issue is that your constructor needs a generic type. Right now it works exclusively with Event so it can't handle subtypes of Event. Kotlin doesn't (yet?) support generic types for constructors so you have to do this with a factory function.
Here's an example of all the above.
class MyBus() : EventBus {
private val eventListeners: MutableMap<Class<*>, MutableList<EventListener<*>>> = mutableMapOf()
override fun <E : Event> registerListener(aClass: Class<E>, eventListener: EventListener<E>) {
val listeners = retrieveListeners(aClass)
listeners.add(eventListener)
}
private fun <E: Event> retrieveListeners(aClass: Class<E>): MutableList<EventListener<E>> {
#Suppress("UNCHECKED_CAST")
return eventListeners.getOrPut(aClass) { mutableListOf() } as MutableList<EventListener<E>>
}
}
// Factory function
fun <E : Event> myBusOf(listeners: List<Pair<Class<E>, EventListener<E>>>): MyBus {
return MyBus().apply {
listeners.forEach {
registerListener(it.first, it.second)
}
}
}
And you might want to change the type of the factory parameter from a <List>Pair to a vararg Pair so it's easier to use.
Here's a stripped down example to explain the variance limitation.
Your interface for an Event consumer:
interface EventListener<E : Event> {
fun handle(event: E)
}
Two implementations of Event:
class HelloEvent: Event {
fun sayHello() = println("Hello world")
}
class BoringEvent: Event {}
A class implementing the interface:
class HelloEventListener: EventListener<HelloEvent> {
override fun handle(event: HelloEvent) {
event.sayHello()
}
}
Now you have an EventListener that can handle only HelloEvents. Try to treat it like an EventListener<Event>:
val eventListener: EventListener<Event> = HelloEventListener() // COMPILE ERROR!
Imagine the compiler did not prevent you from doing this and you do this:
val eventListener: EventListener<Event> = HelloEventListener()
eventListener.handle(BoringEvent()) // CLASS CAST EXCEPTION AT RUN TIME!
If this were allowed your HelloEventListener would try to call sayHello() on the BoringEvent, which doesn't have that function, so it will crash. This is what generics are here to protect you from.
Now suppose your HelloEventListener.handle() didn't call event.sayHello(). Well, then it could have safely handled a BoringEvent. But the compiler isn't doing that level of analysis for you. It just knows what you declared, that HelloEventListener cannot handle anything except HelloEvent.

Hiding base class constructor parameters in Kotlin

I am trying to understand how to hide a base constructor parameter in a subclass in kotlin. How do you put a facade over a base constructor? This doesn't work:
import com.android.volley.Request
import com.android.volley.Response
class MyCustomRequest(url: String)
: Request<String>(Request.Method.POST, url, hiddenListener) {
private fun hiddenListener() = Response.ErrorListener {
/* super secret listener */
}
...
}
I think I understand the problem:
During construction of a new instance of a derived class, the base
class initialization is done as the first step (preceded only by
evaluation of the arguments for the base class constructor) and thus
happens before the initialization logic of the derived class is run.
I'm trying to solve this problem for Volley, where I need my custom request to be be a Request so that it can be passed into a RequestQueue. It would be easier of RequestQueue took in some kind of interface but since it doesn't I have to subclass. There are other ways I can hide these complexities from the caller, but this limitation has come up for me other times in Kotlin and I'm not sure how to solve it.
I am not familiar with volley but I tried to come up with an example that should give you some insight how to solve your problem. What you can do is use a companion object:
interface MyListener {
fun handleEvent()
}
open class Base<T>(anything: Any, val listener: MyListener) { // this would be your Request class
fun onSomeEvent() {
listener.handleEvent()
}
}
class Derived(anything: Any) : Base<Any>(anything, hiddenListener) { // this would be your MyCustomRequest class
private companion object {
private val hiddenListener = object : MyListener {
override fun handleEvent() {
// do secret stuff here
}
}
}
}
So if you apply this to your problem, the result should look something like this:
class MyCustomRequest(url: String)
: Request<String>(Request.Method.POST, url, hiddenListener) {
private companion object {
private val hiddenListener = Response.ErrorListener {
/* super secret listener */
}
}
...
}
A different way would be to use a decorator, create your Request withing that decorator and just delegate the calls to it:
class Decorator(anything: Any) {
private var inner: Base<Any>
private val hiddenListener: MyListener = object : MyListener {
override fun handleEvent() { }
}
init {
inner = Base(anything, hiddenListener)
}
}
And once again for your example that would look like this:
class MyCustomRequest(url: String) {
private var inner: Request<String>
private val hiddenListener = Response.ErrorListener {
/* super secret listener */
}
init {
inner = Request<String>(Request.Method.POST, url, hiddenListener)
}
...
}

Kotlin cannot access protected abstract method

I have the following classes structure:
abstract class Abstr{
protected abstract fun m()
}
class Child : Abstr(){
private val subChild: Abstr = Child()
override fun m() = subChild.m()// Error:(12, 18) Kotlin: Cannot access 'm': it is protected in 'Abstr'
}
I got an exception Kotlin: Cannot access 'm': it is protected in 'Abstr'
It little bit confusing, because the same structure is legal for java.
According to kotlin docs
protected — visible inside this class only + visible in subclasses too;
Is it bug or expected behavior?
It is designed behavior
Protected modifier in Kotlin similar to Java, but has additional restrictions.
Protected in Java:
Visible for inheritance
Visible in package
Protected in Kotlin:
Visible for inheritance
So, according to the code in question we cannot access protected method
class Child : Abstr(){
private val subChild: Abstr = Child()
override fun m() = subChild.m() //Trying to access not inherited method
}
There is similar restriction in Java, when we trying to access protected member from another package:
// FILE: a/SuperClass.java
package a;
public class SuperClass {
protected void superFunction() {}
}
// FILE: b/ChildClass.java
package b;
public class ChildClass extends SuperClass {
void testFunction() {
((SuperClass) new ChildClass()).superFunction(); // ERROR: superFunction() has protected access in a.SuperClass
}
}
There is answer in issue tracker from Kotlin team:
https://youtrack.jetbrains.com/issue/KT-21048
The current behavior is by design.
By calling subChild.m() you're trying to access an instance of Abstr from outside the object, so protected access prevents you from doing this.
Let me show you a short example to clarify the case
abstract class ParentCl {
protected var num = 1
protected open fun m(){
}
}
class ChildCl : ParentCl() {
private val a0 : ParentCl = ChildCl()
override fun m() {
super.m() // 1-st case
num = 2 // 2-nd case
a0.m() // 3-rd case
}
}
You're calling the protected ParentCl's fun from the child class. It will work fine.
You're modifying of protected variable from the child class. It will work fine.
You're calling the protected fun outside the context of the child class. This will not work.
Depends on what was your goal there are two solutions:
If you wanted to call m() from ParentCl you need to change the visibility from protected to internal or public.
If you wanted to call m() from the child class you need to declare the variable without the explicit type of its parent: private val subChild = Child().
Note: in case you will use m() from other children of ParentCl you need to enlarge the visibility scope inside child class: public override fun m() {...}
It's possible a bug.
When i added {} everything became ok.
abstract class Abstr{
protected abstract fun m()
}
class Child : Abstr(){
private val subChild: Abstr = Child()
override fun m() {
subChild.m() // Compiles fine
}
}
Create issue on https://discuss.kotlinlang.org/
or write on Slack
http://slack.kotlinlang.org/
protected — same as private + visible in subclasses too;
abstract class BaseActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(getLayoutResId())
}
protected open fun getPageTitle() = SpannableString(getString(R.string.app_name))
}
In your sub class
class EditProfileActivity : BaseActivity() {
override fun getPageTitle(): SpannableString = SpannableString(getString(R.string.edit_profile))
}
See the doc for more details
visibility-modifiers
Classes