I wrote next class (the simpliest version to show ...
# announcements
m
I wrote next class (the simpliest version to show the mistake)
Copy code
abstract class MockedMethod<in I, out O> {
    abstract fun execute(arg: I): O
    abstract fun combine(method: MockedMethod<I, O>): MockedMethod<I, O>
}
But I get error for combine argument:
Copy code
Type parameter I is declared as 'in' but occurs in 'out' position in type MockedMethod<I, O>
Type parameter O is declared as 'out' but occurs in 'in' position in type MockedMethod<I, O>
I don't understand why there is error. What is wrong?
For example:
Copy code
class A

class A1: A()

class B

class B1: B()

val method1 = MockedMethod<A1, B>()
val method2 = MockedMethod<A, B1>()

val combined: MockedMethod<A1, B> = method1.combine(method2) //correct, isn;t it? Because we can cast A1 to A and B1 to B
k
because
I
and
O
are both used in both in and out positions (accepted by
combine
and returned by
combine
)
r
(no, that doesn't actually help)
Copy code
abstract class MockedMethod<in I, out O> {
    abstract fun execute(arg: I): O
    abstract fun combine(method: MockedMethod<I, Any>): MockedMethod<Any, Any>
}
this, and various variants to it, throw too
trying to think why would that ↑ throw, but unsure yet
a
This will go pass the compiler error:
Copy code
abstract class MockedMethod<in T, out R> {
    abstract fun execute(arg: T): R
    abstract fun combine(mockedMethod: MockedMethod<Any, Any>): MockedMethod<T, R>
}
May be a bit tricky to handle the call the combine as you have no type safety any more.
Also this is also a work around, but you will loose the combine method in the inheritance hierarchy:
Copy code
abstract class MockedMethod<in T, out R> {
    abstract fun execute(arg: T): R
}

fun <T,R> MockedMethod<T,R>.combine(m: MockedMethod<T,R>): MockedMethod<T, R> {
    return object : MockedMethod<T,R>() {
        override fun execute(arg: T): R {
            TODO("Implement combine")
        }
    }
}
m
I found another workaround:
Copy code
abstract class MockedMethod<I, O> {
    abstract fun execute(arg: I): O
    abstract fun combine(method: MockedMethod<in I, out O>): MockedMethod<I, O>
}
But it looks weird. Why is there error when
in
and
out
in class definition, but no error when they are in method definition? Looks like compiler bug.
And found old one with similar issue: https://youtrack.jetbrains.com/issue/KT-7305
Checked Scala compiler: the same issue
Oh! I catched it! Let's assume there is equivalent interfaces and classes and contravariant type
I
is allowed:
Copy code
interface Processor<in I, out O> {
    fun setArg(arg: I)
    fun process(fn: (I) -> O): O
}

interface A {
    val a: Int
}

interface B: A {
    val b: Int
}

class ProcessorImpl: Processor<A, Int> {
    lateinit var value: A
    
    override fun setArg(a: A) { value = a }    
    override fun process(fn: (A) -> Int): Int = fn(value)
}

fun main() {
    val p1: Processor<A, Int> = ProcessorImpl()
    p1.setArg(object: A { override val a = 10 }) //OK: type argument is compartible
    val p2: Processor<B, Int> = p1 //OK: B can be casted to A and A is contravariant
    p2.process { it: B -> it.b } //Ohhhh... Function expects B, but there is A as argument
}
So, Kotlin (and Scala) rejects this case absolutely correctly.