Problem with generics in Kotlin

I have issues with Kotlin generics system. I cannot figure out how to define constructor in kotlin to make it compilable. As far as I know similar example would work in Java, but it does not in Kotlin.

This is working example, but I do not want to make my ProxyIterator generalized with not directly needed type X:

class ProxyIterator<X, T>(private val items : Iterator<X>, private val mapper: ((X) -> Iterable<T>?)? = null) : Iterator<T> {
    override fun hasNext(): Boolean {
        TODO("Not yet implemented")
    }

    override fun next(): T {
        TODO("Not yet implemented")
    }
}

fun mapperFun(index: Int) : Collection<String> = listOf("Just String")

fun funB() {
    ProxyIterator(listOf(1,2,3).iterator(), ::mapperFun)
}

Any Ideas?

I doubt the first code sample would work in Java. ProxyIterator requires a mapper that should be able to accept Any? objects, but you provide a function that does not work with Any?, but only with integers.

But I get your point about X type that is only needed when constructing the object and then it is redundant. One solution is to create interface with T only and private implementation with both T and X. Then you need a function parameterized with both T and X, but returning object with only T. Maybe there is a better solution.

If your ProxyIterator does not provide anything more than just Iterator, then you can make it simpler, treating ProxyIterator as this private implementation. Just make it private and then add this function:

fun <X, T> proxyIterator(items : Iterator<X>, mapper: ((X) -> Iterable<T>?)? = null) : Iterator<T> {
    return ProxyIterator(items, mapper)
}

Unfortunatelly it requires ProxyIterator itself to be generalized with extra type, which I want to avoid as mapper is a purely auxliary function.

I believe it is a problem of language design: any type should be reducible to Any?

No, it shouldn’t. ProxyIterator says it will provide Any? objects to the mapper function. It could be an integer, it could be a sheep. Now, your mapperFun() can only work with integers, not with sheep. Therefore, it doesn’t fit in this place. The opposite would work: ProxyIterator would require (Int) -> Iterable<T> and you would provide a function that accepts Any?.

If you need this ProxyIterator type then do it like this:

fun <X, T> proxyIterator(...) : ProxyIterator<T> = ProxyIteratorImpl<X, T>(...)

interface ProxyIterator<T> : Iterator<T> { ... }

private class ProxyIteratorImpl<X, T> : ProxyIterator<T> { ... }

Actually, it makes sense that we need two distinct types here. X is necessary for implementation, but it isn’t for the external API.

1 Like

I guess you deleted your post with Java example after you discovered it stops working when the mapper receives Integer, not Object :wink:

Look, this is pretty easy, but you need to understand what is the data flow here. Let’s make a simpler example:

fun main() {
    outer(::inner)
}

fun outer(func: (Any?) -> Unit) {
    func("hello")
}

fun inner(value: Int) {}

func in outer() receives Any?, meaning that it can be invoked with any possible object. So outer() invokes it passing a String. String is Any?, so func should handle it just right. Now, we pass inner as func. But wait… as a result we do something like this: inner("hello") which doesn’t make sense, because inner() can only accept integers. This is why outer(::inner) does not compile.

(Int) -> Unit can’t be safely cast to (Any?) -> Unit. The opposite is fine.

I don’t want to start talking about variance, because it will only make things more complicated. But the point is: casting (Int) -> Unit to (Any?) -> Unit is actually like casting Any? to Int. That’s because Any?/Int is consumed here, not produced.

The source of the problem seems to be that it is impossible in Kotlin to generalize constructor not generalizing class itself. In Java it works.

This Java compiles:

Java Code

public class ProxyIterator<T> implements Iterator<T> {

    public <X> ProxyIterator(List<Object> items, Function<X, Iterator<T>> mapper) {
    }

    @Override
    public boolean hasNext() {
        return false;
    }

    @Override
    public T next() {
        return null;
    }

    public static class Mappers {
        public static Iterator<String> strMapper(Integer obj) {
            return Arrays.asList("" + obj).iterator();
        }
    }

    public void testGenerics() {
        ProxyIterator<String> proxyIterStr = new ProxyIterator<String>(Arrays.asList(1,2,3), Mappers::strMapper);
    }
}

Unfortunatelly In Kotlin it seems to be impossible :frowning:

Indeed :relaxed:

Yes, I noticed that we (probably?) can’t generalize the constructor in Kotlin. This is why I suggested using additional function instead of constructor. It can be a top level function, it can be a member of companion object. Other than that, I think you can do the same in Kotlin, as in Java.

I mean, if you only need this X in the constructor and then you don’t use it internally in ProxyIterator, then you don’t even have to create a separate ProxyIterator<T> and ProxyIteratorImpl<X, T> as I suggested. Just do exactly as you would in Java, but move the code from the constructor to the external function (and make the constructor private). I think it should be fine. You can even name your function ProxyIterator() create invoke operator in the companion and then it feels almost like the constructor :slight_smile:

2 Likes

Yes, eventually I have achived what I wanted with companion oject. It looks really scarry though :slight_smile:

Thank you for your help!