Kotlin - How to find and cast an element by its type - kotlin

I have a collection of objects which inherit Component and I want a function which finds an object by its concrete class and return it.
But Kotlin does not like the cast I do, and adding #Suppress("UNCHECKED_CAST") is ugly.
I have the following code:
open class GameObjectImpl : GameObject {
private val attachedComponents = mutableSetOf<Component>()
#Suppress("UNCHECKED_CAST")
override fun <TComponent : Component> getComponent(type: KClass<TComponent>): TComponent? {
return attachedComponents.find { type.isInstance(it) } as? TComponent
}
}

This should work for you:
open class GameObjectImpl : GameObject {
val attachedComponents = mutableSetOf<Component>()
override inline fun <reified TComponent : Component> getComponent(type: KClass<TComponent>): TComponent? {
return attachedComponents.filterIsInstance<TComponent>().firstOrNull()
}
}

Related

Kotlin: use generic on interface level as argument type for function

Is it impossible to use generic on interface level as argument type for function?
I read about out and in keywords but as I understand they don't work for this case.
interface BaseB
open class ChildB1: BaseB
open class ChildB2: BaseB
abstract class BaseMapper<V: BaseB> {
open fun test(v: V) {
return
}
}
class TestMapper1: BaseMapper<ChildB1>() {
override fun test(v: ChildB1) {
return
}
}
class TestMapper2: BaseMapper<ChildB2>() {
override fun test(v: ChildB2) {
return
}
}
#Test
fun t() {
//ERROR
val mappers: List<BaseMapper<BaseB>> = listOf(TestMapper1(), TestMapper2())
mappers[0].test(ChildB1())
}
A BaseMapper<ChildB1> is not logically a BaseMapper<BaseB>. It consumes ChildB’s, so if you passed some other implementation of Base it would cause a ClassCastException if the compiler let you do that. There is no common subtype of your two subclasses besides Nothing, so the only way to put both of these types in the same list is to make it a List<BaseMapper<in Nothing>>.
Example of why it is not logically a BaseMapper<BaseB>:
open class ChildB1: BaseB {
fun sayHello() = println("Hello world")
}
class TestMapper1: BaseMapper<ChildB1>() {
override fun test(v: ChildB1) {
v.sayHello() // if v is not a ChildB1, this would be impossible
}
}
//...
val impossibleCast: BaseMapper<BaseB> = TestMapper1()
// TestMapper1 cannot call sayHello() because it's undefined for ChildB2.
// This is impossible:
impossibleCast.test(ChildB2())
// ...so the compiler prevents you from doing the impossible cast in the first place.

Generics in Objects

I have a question about sealed class, generics and object.
Let's say I would like to model something like 3 finite cases with a sealed class something like this:
sealed class ChangeState<S> {
fun reduceState(state: S): S
}
data class SetState<S>(val newState: S) : ChangeState<S>() {
override fun reduce(state: S): S = newState
}
object NoStateChange : ChangeState<Nothing>() { // What do I specify here for ChangeState? Nothing?
override fun reduce(state: Nothing): Nothing {
throw Exception("This should never be called")
}
}
The goal is to provide a convenient way to define NoStateChange in a generic way that it can be used as following:
fun foo(i : Int) : ChangeState<Int> {
return if (i==0)
NoStateChange // Won't compile because return type is ChangeState<Nothing> but expected ChangeState<Int>
else
SetState(i)
}
Is there a way to do that with object and Generics somehow?
As pointed out by #Tenfour04 the issue is that out is needed but reduceState() would require in as well. However, reduceState() can be refactored out of the class hierarchy and moved to an extension function like that:
sealed class ChangeState<out S>
data class SetState<S>(val newState: S) : ChangeState<S>()
object NoStateChange : ChangeState<Nothing>()
fun <S> ChangeState<S>.reduce(state: S): S {
return when (val change = this) {
is SetState -> change.newState
is NoStateChange -> state
}
}

Kotlin generics supertype not applied

I was coding on Java for quite a long time and trying to migrate to Kotlin. I'm confused with Generics in Kotlin a bit...
I have a DelegateManager class. It should consume only subtypes of IViewData
class DelegateManager<T : IViewData> {
private val delegates: MutableList<AdapterDelegate<T>> = mutableListOf()
fun addDelegate(adapterDelegate: AdapterDelegate<T>) {
delegates.add(adapterDelegate)
}
...
}
Inside TrackListAdapter I want to add a delegate. As you might have seen it's AdapterDelegate<TrackViewData> and TrackViewData is a subtype of IViewData So it should work but it shows error inside init block of TrackListAdapter
class TrackListAdapter : BaseListAdapter<IViewData>() {
init {
delegateManager.addDelegate(TrackViewDelegate()) // error: Type mismatch -> Required: AdapterDelegate<IViewData>, Found: TrackViewDelegate
}
}
class TrackViewDelegate : AdapterDelegate<TrackViewData>() {
override fun onCreateViewHolder(parent: ViewGroup): ListViewHolder<TrackViewData> {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.track_item, parent, false)
return TrackViewHolder(itemView)
}
override fun isDelegateForDataType(data: IViewData) = data is TrackViewData
}
How to deal with it? How to extend the generic parameter correctly?

How do I cast custom MutableLiveData to custom LiveData?

suppose there are 2 classes:
class MyLiveData:LiveData<Int>()
class MyMutableLiveData:MutableLiveData<Int>()
Casting from MutableLiveData to LiveData is permitted:
val ld1=MutableLiveData<Int>()
val ld2:LiveData<Int> = ld1 //ok
But you can't cast your own implementations this way:
val mutable=MyMutableLiveData()
val immutable:MyLiveData = mutable //type missmatch
I understand that MutableLiveData extends LiveData thats why they are castable.But I can't have MyMutableLiveData extending MyLiveData as it won't be mutable in this case
Are there any workarounds?
UPD:I guess I need to show motivation of extending LiveData.I'm trying to implement MutableLiveDataCollection which notifies not just value changes via setValue/postValue but also value modification like adding new elements.I'm surprised there is no native solution for this.
Anyway to obseve modify events there have to be additional observe method.And this method have to be inside immutable part aka LiveDataCollection because views will call it.Inheritance is natural solution here IMHO.
The key idea sits in the MutableLiveData class.The only thing this class does - is it changes access modifiers on setValue/postValue methods.I can do the same trick.Therefore the final code will be:
open class LiveDataCollection<K,
L:MutableCollection<K>,
M:Collection<K>>: LiveData<L>() {
private var active=false
private var diffObservers = ArrayList<Observer<M>>()
fun observe(owner: LifecycleOwner, valueObserver: Observer<L>, diffObserver: Observer<M>) {
super.observe(owner,valueObserver)
diffObservers.add(diffObserver)
}
protected open fun addItems(toAdd:M) {
value?.addAll(toAdd)
if (active)
for (observer in diffObservers)
observer.onChanged(toAdd)
}
override fun removeObservers(owner: LifecycleOwner) {
super.removeObservers(owner)
diffObservers= ArrayList()
}
override fun onActive() {
super.onActive()
active=true
}
override fun onInactive() {
super.onInactive()
active=false
}
}
class MutableLiveDataCollection<K,L:MutableCollection<K>,
M:Collection<K>>: LiveDataCollection<K,L,M>() {
public override fun addItems(toAdd:M) {
super.addItems(toAdd)
}
public override fun postValue(value: L) {
super.postValue(value)
}
public override fun setValue(value: L) {
super.setValue(value)
}
}

Kotlin secondary constructor with generic type

In java
I can achieve two constructors like
public TargetTitleEntryController() { }
public <T extends Controller & TargetTitleEntryControllerListener> TargetTitleEntryController(T targetController) {
setTargetController(targetController);
}
I want to convert it to Kotlin
class TargetTitleEntryController ()
with the secondary constructor. I don't know how to declare with generic type like Java counterpart.
There is no intersection types in Kotlin (sad)
But there is Generic constraints (hope)
But Generic constraints not applicable in the secondary constructor (sad)
But you can simulate secondary constructor in a companion object using Invoke operator overloading (workaround):
class TargetTitleEntryController {
// ...
companion object {
operator fun <T> invoke(targetController: T): TargetTitleEntryController
where T : Controller,
T : TargetTitleEntryControllerListener {
return TargetTitleEntryController().apply {
setTargetController(targetController)
}
}
}
}
Here is an example where you specify a Type T which implements two interfaces (CharSequence, Runnable):
class Person<T>(val name: String) where T : CharSequence, T : Runnable {
constructor(name: String, parent: T) : this(name) {
}
}
So actually something like this should work:
class TargetTitleEntryController<T> () where T : Controller, T : TargetTitleEntryControllerListener {
constructor(targetController: T) : this() {
}
}
You can do it like this :)
class TargetTitleEntryController <T>() : Controller() where T: Controller, T: TargetTitleEntryControllerListener<T> {
constructor(target: T) : this() {
targetController = target
}
}
you can implement it in your parent controller like this:
class TargetDisplayController : Controller(), TargetTitleEntryControllerListener<TargetDisplayController> {
var targetTitleEntryController = TargetTitleEntryController(this)
override fun onTitlePicked(String option) {
}
override fun onAttach(view: View) {
// push controller here
}
}