How do you extend java methods with primitive types (as parameter or return type)?


#1

Hello all,

my problem is that I am having a lot of java classes (due to swig) which only have the
SomeType1 get(int index);
long size();
member methods.

Lets call this java ArrayClass1
In order to be able to do simple for(item in ) I need to override the iterator() operator.

Implementing

ArrayClass1Iterator(val array: ArrayClass1, var currentIndex: Int = 0): Iterator<SomeType1> {
  override fun hasNext(): Boolean {
    return currentIndex < array.size() - 2
  }

  override fun next(): SomeType1 {
    return array[currentIndex++]
  }
}

operator fun ArrayClass1.iterator(): ArrayClass1Iterator {
    return ArrayClass1Iterator(this)
}

works, but since I’m having A LOT of these ArrayClasses I would like to have a generic implementation. Then I’d write:

class ArrayIterator<S, out T>(val array: T, var currentIndex: Int = 0): Iterator<S> {
    override fun hasNext(): Boolean {
        return currentIndex < array.size() - 2
    }

    override fun next(): S {
        return array.get(currentIndex++)
    }
}

private operator fun <T, S> T.get(index: Int): S {
    return null!!
}

private fun <T> T.size(): Long {
    return 0
}

operator fun ArrayClass1.iterator(): ArrayIterator<SomeType1, ArrayClass1> {
    return ArrayIterator(this)
}

I only extended the size and get methods as well. because else it doesn’t allow me to call these methods on the instance of T. But now it no longer calls the get and size methods on the ArrayClass1 but simply these dummy extensions, even though the kotlin docs say that if the same method is present in the given class, than that one will be called instead of the extension function. I guess this has to do with the fact that kotlin.Long and the primitive type “long” are not the same, and most likely the same goes for kotlin.Int and “int”.

Is there any way to get this to work?


#2

Could you format your code properly? https://help.github.com/articles/basic-writing-and-formatting-syntax/#quoting-code
This would make understand your problem much more easy :slight_smile:


#3

I think I understand what you are trying to do, but let me be sure. So let me explain the problem.
By adding the extension get and size to a type-parameter T you basically create a new method get for every type. Now you call get on your array. As array already has a method called get you think this method should get called instead of your dummy extension method. This is not the case, because of the type erasure. The problem is, at runtime Kotlin does not know anything about the type of your array. It no longer knows that it already has a method called get and thats why the extension method is called. What you would need to do is have your array class extend an interface with the methods you want to call. If that is not possible, you could maybe try to generate the iterators using some sort of code generation (https://kotlinlang.org/docs/reference/kapt.html).


#4

Sorry. Should be better now.


#5

What you can do is add a specialization for ArrayClass1 (that has a get) - but you would have to do it for every class :disappointed:.
Do the classes you have in C/C++ actually have some common parent?
What you want is some form of code generation that actually adds the right methods to ArrayClass1.

Btw. for typesafety it is probably better to have the iterator work different. It either uses a proxy or it uses
lambda’s to get the size and value (btw be careful whether the index is long or int):

Option1:

  interface <out T> ArrayWrapper<T> {
    val size():Long
    fun get(index:Long) T
  }

  class ArrayIterator<out T>(val array: ArrayWrapper<T>, var currentIndex:Long = 0): Iterator<T> {
    // obvious class body
  }

Option2:

  class ArrayIterator<A, out T>(val array: A, val size: A.()->Long, val get: A.(Long)->T, var currentIndex:Long = 0): Iterator<T> {
    override fun hasNext(): Boolean = currentIndex < array.size() -2

    override fun next(): T = array.get(currentIndex++)
  }

(I haven’t compiled the code, so it could be that the lambda names are problematic, anything unique should fix that).

What you are asking for can be easily done by typeclasses (which actually involves code generation)


#6

Your second option worked like a charm.
Now I “simply” have to generate the iterator extension, but at least the ArrayIterator class can be used untouched, for every ArrayClassX I have.
Thanks a lot.