Invoking methods on interfaces with generics - kotlin

The following is a very simple illustration of what I'm trying to do:
interface Event {
fun value(): Int
}
class Event1: Event {
override fun value() = 1
}
class Event2: Event {
override fun value() = 2
}
interface EventConsumer<T> where T: Event {
fun consume(event: T)
}
class Event1Consumer: EventConsumer<Event1> {
override fun consume(event: Event1) {
println(event.value())
}
}
class Event2Consumer: EventConsumer<Event2> {
override fun consume(event: Event2) {
println(event.value())
}
}
class EventManager {
private val consumers: Map<KClass<*>, EventConsumer<*>> = mapOf(
Event1::class to Event1Consumer(),
Event2::class to Event2Consumer()
)
fun consume(event: Event) {
val consumer = consumers[event::class]
consumer?.consume(event)
}
}
The final method call (consumer.consume()) is giving me a compiler error
Out-projected type 'EventConsumer<*>?' prohibits the use of 'public
abstract fun consume(event: T): Unit defined in EventConsumer'
I know that Kotlin is a lot more strict about generics than Java which is probably why it doesn't work, but how would I implement something like this properly?

Since you are building the consumers map, it would be safe to make an unchecked cast to the correct generic EventConsumer type:
fun <T: Event> consume(event: T) {
val consumer = consumers[event::class] as? EventConsumer<T>
consumer?.consume(event)
}

Related

Is it possible to verify at compile time whether the required function is called for the Factory Class in Kotlin?

class ModelFactory {
fun setA() : ModelFactory {
// blabla...
}
fun setB() : ModelFactory {
// blabla...
}
fun setC() : ModelFactory {
// blabla...
}
fun build() : Model {
// An error occurs if any of setA, setB, and setC is not called.
}
}
//example
fun successTest() {
ModelFactory().setA().setB().setC().build() // No error occurs at compile time
}
fun failTest() {
ModelFactory().setA().build() // An error occurs at compile time because setB and setC are not called.
}
It's awkward grammatically, but I think it's been expressed what I want.
I have already implemented an error-raising runtime for this requirement, but I want to check this at compile time.
If possible, I think I should use annotations. But is this really possible at compile time?
With Kotlin, I have been avoiding builder pattern, as we can always specify default values for non-mandatory fields.
If you still want to use a builder pattern, you can use Step builder pattern that expects all mandatory fields to be set before creating the object. Note that each setter method returns the reference of next setter interface. You can have multiple Step builders based on the combination of mandatory fields.
class Model(val a: String = "", val b: String = "", val c: String = "")
class StepBuilder {
companion object {
fun builder(): AStep = Steps()
}
interface AStep {
fun setA(a: String): BStep
}
interface BStep {
fun setB(b: String): CStep
}
interface CStep {
fun setC(c: String): BuildStep
}
interface BuildStep {
//fun setOptionalField(x: String): BuildStep
fun build(): Model
}
class Steps : AStep, BStep, CStep, BuildStep {
private lateinit var a: String
private lateinit var b: String
private lateinit var c: String
override fun setA(a: String): BStep {
this.a = a
return this
}
override fun setB(b: String): CStep {
this.b = b
return this
}
override fun setC(c: String): BuildStep {
this.c = c
return this
}
override fun build() = Model(a, b , c)
}
}
fun main() {
// cannot build until you call all three setters
val model = StepBuilder.builder().setA("A").setB("B").setC("C").build()
}

How to return T in Kotlin?

I want to return T in the function.
I have an interface class IRepository.kt
interface IRepository
{
fun <T>Save(model:T)
fun <T>Delete(model:T)
fun <T>Get(id:Long):T
}
I want to implement in Repolmpl.kt
class Repolmpl:IRepository
{
override fun <T>Delete(model:T)
{
println("$model : Save}")
}
override fun <T>Get(id:Long):T
{
return T //ERROR here I want to return T...
}
override fun <T> Save(model: T)
{
println("$model : Delete")
}
}
I saw some similar questions online but I just can't find the right solution.
A generic type T is basically just a template. You cannot return it but have to replace it with an actual type first. Make the interface itself generic, not its methods. When implementing, specify T:
interface IRepository<T> {
fun save(model: T)
fun delete(model: T)
fun get(id: Long): T
}
class Repolmpl: IRepository<String>
{
override fun delete(model: String) {}
override fun get(id: Long): String {}
override fun save(model: String) {}
}
You cannot just return T. T is type here, and it is like return String.
You have to return instance of T. So, sth like:
class Repo {
val data = mapOf<Long, Any>()
// ...
fun <T> get(id: Long): T {
return data[id] as T // Get data from somewhere and then cast it to expected type
}
}

How to inherit MutableList in Kotlin?

I am trying to inherit MutableList, and add my own function to it. For example:
class CompositeJob : MutableList<Job> {
fun cancelAllJobs() {
for (job in this) {
job.cancel()
}
}
}
But I got the following error:
Class 'CompositeJob' is not abstract and does not implement abstract member
public abstract val size: Int defined in kotlin.collections.MutableList
How can I inherit MutableList, so I can use its original methods like add() and isEmpty(), and add my own one?
Thanks.
One option other answers don't mention is delegation:
class CompositeJob : MutableList<Job> by mutableListOf() {
fun cancelAllJobs() {
for (job in this) {
job.cancel()
}
}
}
is basically equivalent to
class CompositeJob : MutableList<Job> {
private val impl: MutableList<Job> = mutableListOf()
override fun size() = impl.size()
override fun add(x: Job) { impl.add(x) }
// etc for all other MutableList methods
fun cancelAllJobs() {
for (job in this) {
job.cancel()
}
}
}
MutableList is an interface - it doesn't implement any of its methods, just declares them. If you want to implement MutableList from scratch, you'll have to implement all 20 of its methods plus the size property, as your error already told you.
You can, however, subclass actual implementations of this interface, for example ArrayList or LinkedList:
class CompositeJob : ArrayList<Job>() {
fun cancelAllJobs() {
for (job in this) {
job.cancel()
}
}
}
Edit: If you're just looking to group coroutine Job instances, you should use a parent Job, a SupervisorJob, and CoroutineScope at this point, instead of collecting jobs like this manually.
Implementation of all the members is not the only way to achieve what you need.
An easier way is create an extension function like this:
fun MutableList<Job>.cancelAllJobs() {
this.forEach { it.cancel() }
}
Now you can call myList.cancelAllJobs() for every MutableList<Job> object.
As I have tested you need to make CompositeJob an abstract as error also suggest. After that, you can able to override add() and isEmpty() from MutableList
So your code looks like as below:
abstract class CompositeJob : MutableList<Job> {
override fun add(element:Job): Boolean
{
}
override fun isEmpty(): Boolean
{
}
}
If you want to avoid inheritance, or if the concrete class methods are marked final, then you can use composition and redirect all method calls which are not overridden. Here's a template:
class MyMutableList<T> : MutableList<T> {
private val list = mutableListOf<T>()
override val size get() = list.size
// MutableList<T>
override fun add(element: T): Boolean = list.add(element)
override fun add(index: Int, element: T) = list.add(index, element)
override fun addAll(elements: Collection<T>): Boolean = list.addAll(elements)
override fun addAll(index: Int, elements: Collection<T>): Boolean = list.addAll(index, elements)
override fun clear() = list.clear()
override fun remove(element: T): Boolean = list.remove(element)
override fun removeAll(elements: Collection<T>): Boolean = list.removeAll(elements)
override fun removeAt(index: Int): T = list.removeAt(index)
override fun retainAll(elements: Collection<T>): Boolean = list.retainAll(elements)
override fun set(index: Int, element: T): T = list.set(index, element)
// List<T>
override fun contains(element: T): Boolean = list.contains(element)
override fun containsAll(elements: Collection<T>): Boolean = list.containsAll(elements)
override fun get(index: Int): T = list.get(index)
override fun indexOf(element: T): Int = list.indexOf(element)
override fun isEmpty(): Boolean = list.isEmpty()
override fun iterator(): MutableIterator<T> = list.iterator()
override fun lastIndexOf(element: T): Int = list.lastIndexOf(element)
override fun listIterator(): MutableListIterator<T> = list.listIterator()
override fun listIterator(index: Int): MutableListIterator<T> = list.listIterator(index)
override fun subList(fromIndex: Int, toIndex: Int): MutableList<T> =
list.subList(fromIndex, toIndex)
}
...of course, this might add an additional layer of indirection, possibly affecting list performance with a slight overhead.

Typesafe map assignment and retrieval

I am trying to proxy calls for Observables and LiveData (similar to the Mediator pattern), but I could not find a typesafe solution. This is the problem:
class Proxy {
private val backupMap = HashMap<LiveData<Any>, Observer<Any>>()
fun <T> add(liveData : LiveData<T>, observer : Observer<T>) {
// !This is the issue LiveData<Any> is expected
backupMap.put(liveData, observer)
}
fun attach() {
backupMap.forEach { (key, value) ->
key.observeForever(value)
}
}
}
fun addSome() {
Proxy().apply {
add(MutableLiveData<String>(), Observer { })
}
}
I could cast backupMap.put to backupMap.put(liveData as LiveData<Any>, observer as Observer<Any>) but this causes an Unchecked Cast.
Solution I found is to use an intermediate object to hold the typesafe binding:
private val backupMap: MutableMap<LiveData<*>, Attacher<*>>
private class Attacher<A>(private val lifeData: LiveData<A>, private val observer : Observer<A>) {
fun attach() {
lifeData.observeForever(observer)
}
fun detach() {
lifeData.removeObserver(observer)
}
}

Replacing SAM-constructor with lambda with covariant type

I have got the following Java interfaces:
interface Action1<T> {
void call(T t);
}
interface Test<T> {
void test(Action1<? super T> action)
}
And the following Kotlin class:
interface A {
fun go()
}
abstract class Main {
abstract fun a(): Test<out A>
fun main() {
a().test(Action1 { it.go() })
a().test { it.go() }
}
}
Now in the function main, the first statement compiles, but IntelliJ gives a warning that the SAM-constructor can be replaced with a lambda.
This would result in the second statement.
However, this second statement does not compile, because it has type Any?, not A. Removing the out modifier makes it compile again.
Why does this happen?
The use case of this is when the implementing class of Main needs to return Test<B> for the function a(), where B implements A:
class B : A {
override fun go() {
TODO()
}
}
class MainImp : Main() {
override fun a(): Test<out A> {
val value: Test<B> = object : Test<B> {
override fun test(action: Action1<in B>?) {
TODO()
}
};
return value
}
}
It is a compiler bug. You can track it here: https://youtrack.jetbrains.com/issue/KT-12238.