Typed class by typed class without unchecked cast error

#1

Hi! Can i do this without unchecked cast error in last line?

open class Information
class CertainInformation : Information() {
    val result = "TaDa"
}

interface Worker<T: Information> {
    fun run(info: T): String
}

class CertainWorker : Worker<CertainInformation> {
    override fun run(info: CertainInformation): String {
        return info.result
    }
}

interface Runner<T: Worker<*>, I: Information> {
    fun run(worker: T, info: I)
}

class RunnerImpl<T: Worker<I>, I: Information> : Runner<T, I> {
    override fun run(worker: T, info: I) {
        println(worker.run(info))
    }
}

fun main(args: Array<String>) {
    val runner = RunnerImpl<Worker<Information>, Information>()
    runner.run(CertainWorker() as Worker<Information>, CertainInformation())
}

In this example i try to create universal Runner that can work with all types of Workers, but Workers must work with known type of Information.

Thanks!

#2

You need to tighten the link between the worker and the information. In other words, your Runner interface is underspecified. Instead of your current declaration do this:

interface Runner<T: Worker<in I>, I: Information>
#3

@pdvrieze thank you for the help. But i get the same warning.
Warning:(29, 32) Kotlin: Unchecked cast: CertainWorker to Worker

#4

I decided to open Intellij for it. I’m not sure that you need type variables in the Runner type at all. In any case you want to say that it works with any worker where information and worker match. I’ve tried to do this with the following code:


interface Runner<T: Worker<*>, I: Information> {
    fun <U: Worker<J>,J:I>run(worker: U, info: J)
}

class RunnerImpl<T: Worker<I>, I: Information> : Runner<T, I> {
    override fun <U: Worker<J>,J:I> run(worker: U, info: J) {
        println(worker.run(info))
    }
}

fun main(args: Array<String>) {
    val runner = RunnerImpl<Worker<Information>, Information>()
    runner.run(CertainWorker(), CertainInformation())
}

Note that you can just remove the T variable of Runner as it is not actually used at all. Kotlin will not allow the U variable to be a Worker<J> (needed for typesafety) and a Worker<I> (extending T) due to the multiple bounds restriction. I chose I to be the constraint in the code above.

Unfortunately it is not possible to use derived type variables (T.I to mean the information type of the worker of type T. As such it is not possible to precisely describe your constraints because it involves multiple (somewhat coordinated) type variables. But a key part is that the function itself needs to have type variables as it may accept different workers with different (coordinated) info types. This should depend on each worker rather than on each runner.

The only fully typesafe way I can image that would “work” here is some sort of utility class/factory that knows about the different coordinated types but presents a single restriction. The operations on the types then go through this factory/delegate/utility.

#5

@pdvrieze Thanks a lot!!