How to create a custom iterator in kotlin and add to existing class? - kotlin

Hello I am trying to add a custom iterator for example to a Pair class from kotlin package to be able to use instance of that class in a for loop
Let's assume this is what I want to be able to do:
val pair: Pair<Int, String> = Pair(1, "sample data")
for (element in pair) {
println(element)
}
I know that there are plenty other ways to print elements from a pair but I specifically want to be able to use pair in a for loop and I need to add iterator() object with next() and hasNext() methods implementation to Pair class

You can do this by providing the iterator() operator for your object, either as a member function or extension function. Example using an extension function:
fun main() {
val pair: Pair<Int, String> = Pair(1, "sample data")
for (element in pair) {
println(element)
}
}
operator fun <T> Pair<T, T>.iterator(): Iterator<T> = listOf(first, second).iterator()
However, you need to be aware that this way you partially lose strongly typing. element can only be a common supertype of all elements, in most cases simply Any?.
You can read more about this in the official documentation: https://kotlinlang.org/docs/control-flow.html#for-loops

Related

Get index of given element from array extension function kotlin

I'd like to understand Kotlin extension functions more and am trying to implement an extension function for a List, to get the index of an element by passing the value of the position (if that makes sense).
What I have:
fun List<String>.getItemPositionByName(item: String): Int {
this.forEachIndexed { index, it ->
if (it == item)
return index
}
return 0
}
Although this works fine, I would need the same thing for Int too.
To my question, is there a way of combining this into one extension function instead of two seperate ones? I acknowledge that this isn't a lot of code and wouldn't hurt to be duplicated but out of interest and for future references.
I'm aware of this question Extension functions for generic classes in Kotlin where the response is - as I understand it at least - "doesn't quite work like this, but I don't really need it for type but "just" for String and Int.
Kotlin supports what C++ people would refer to as specialization to a certain degree. It works just fine for very basic types like you're using so what you're asking of is definitely possible.
We can declare the following declarations. Of course you could just duplicate the code and you'd be on your way.
public fun List<String>.getItemPositionByName(item: String) = ...
public fun List<Int>.getItemPositionByName(item: String) = ...
If you're not a fan of repeating the code, the idiomatic way would be to make use of file-private functions and simply delegating to the private function.
private fun <T> getItemImpl(list: List<T>, item: T): Int {
list.forEachIndexed { index, it ->
if (it == item)
return index
}
return -1
}
public fun List<String>.getItemPositionByName(item: String) = getItemImpl(this, item)
public fun List<Int>.getItemPositionByName(item: Int) = getItemImpl(this, item)
This limits the getItemImpl which is fully generic to the current file you're in while the Int and String specializations are publicly available anywhere else.
Attempting to call getItemPositionByName on any list which is not of type List<Int> or List<String> will fail with a type error.
Kotlin Playground Link: https://pl.kotl.in/NvIRXwmpU
And just in case you weren't aware, the method you're implementing already exists in the standard library (https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/index-of.html)
The Kotlin standard library already has a function that does this: indexOf().
val one = listOf("a", "b", "c").indexOf("b")
check(one == 1)
One option is to look at the implementation of that function.
There is also the first() function, which you could use if you wanted write your own generic version:
fun <T> List<T>.getItemPositionByName(item: T) = withIndex()
.first { (_, value) -> item == value }
.index
fun main(args: Array<String>) {
val one = listOf("a", "b", "c").getItemPositionByName("b")
check(one == 1)
}
Or, rewriting your original version to use generics:
fun <T> List<T>.getItemPositionByName(item: T): Int {
this.forEachIndexed { index, it ->
if (it == item)
return index
}
return 0
}

Generic inline function

Let's say I have an object which helps me to deserialize other objects from storage:
val books: MutableList<Book> = deserializer.getBookList()
val persons: MutableList<Person> = deserializer.getPersonList()
The methods getBookList and getPersonList are extension functions I have written. Their logic is allmost the same so I thought I may can combine them into one method. My problem is the generic return type. The methods look like this:
fun DataInput.getBookList(): MutableList<Book> {
val list = mutableListOf<Book>()
val size = this.readInt()
for(i in 0 .. size) {
val item = Book()
item.readExternal(this)
list.add(item)
}
return list
}
Is there some Kotlin magic (maybe with inline functions) which I can use to detect the List type and generify this methods? I think the problem would be val item = T() which will not work for generic types, right? Or is this possible with inline functions?
You cannot call the constructor of a generic type, because the compiler can't guarantee that it has a constructor (the type could be from an interface). What you can do to get around this though, is to pass a "creator"-function as a parameter to your function. Like this:
fun <T> DataInput.getList(createT: () -> T): MutableList<T> {
val list = mutableListOf<T>()
val size = this.readInt()
for(i in 0 .. size) {
val item = createT()
/* Unless readExternal is an extension on Any, this function
* either needs to be passed as a parameter as well,
* or you need add an upper bound to your type parameter
* with <T : SomeInterfaceWithReadExternal>
*/
item.readExternal(this)
list.add(item)
}
return list
}
Now you can call the function like this:
val books: MutableList<Book> = deserializer.getList(::Book)
val persons: MutableList<Person> = deserializer.getList(::Person)
Note:
As marstran mentioned in a comment, this requires the class to have a zero-arg constructor to work, or it will throw an exception at runtime. The compiler will not warn you if the constructor doesn't exist, so if you pick this way, make sure you actually pass a class with a zero-arg constructor.
You can't initialize generic types, in Kotlin or Java. At least not in the "traditional" way. You can't do this:
val item = T()
In Java, you'd pass a Class<T> and get the constructor. Very basic example of that:
public <T> void x(Class<T> cls){
cls.getConstructor().newInstance(); // Obviously you'd do something with the return value, but this is just a dummy example
}
You could do the same in Kotlin, but Kotlin has a reified keyword that makes it slightly easier. This requires an inline function, which means you'd change your function to:
inline fun <reified T> DataInput.getBookList(): MutableList<T> { // Notice the `<reified T>`
val list = mutableListOf<T>() // Use T here
val size = this.readInt()
for(i in 0 .. size) {
// This is where the initialization happens; you get the constructor, and create a new instance.
// Also works with arguments, if you have any, but you used an empty one so I assume yours is empty
val item = T::class.java.getConstructor().newInstance()!!
item.readExternal(this) // However, this is tricky. See my notes below this code block
list.add(item)
}
return list
}
However, readExternal isn't present in Any, which will present problems. The only exception is if you have an extension function for either Any or a generic type with that name and input.
If it's specific to some classes, then you can't do it like this, unless you have a shared parent. For an instance:
class Book(){
fun readExternal(input: DataInput) { /*Foo bar */}
}
class Person(){
fun readExternal(input: DataInput) { /*Foo bar */}
}
Would not work. There's no shared parent except Any, and Any doesn't have readExternal. The method is manually defined in each of them.
You could create a shared parent, as an interface or abstract class (assuming there isn't one already), and use <reified T : TheSharedParent>, and you would have access to it.
You could of course use reflection, but it's slightly harder, and adds some exceptions you need to handle. I don't recommend doing this; I'd personally use a superclass.
inline fun <reified T> DataInput.getBookList(): MutableList<T> {
val list = mutableListOf<T>()
val size = this.readInt()
val method = try {
T::class.java.getMethod("readExternal", DataInput::class.java)
}catch(e: NoSuchMethodException){
throw RuntimeException()
}catch(e: SecurityException){
throw RuntimeException()// This could be done better; but error handling is up to you, so I'm just making a basic example
// The catch clauses are pretty self-explanatory; if something happens when trying to get the method itself,
// These two catch them
}
for(i in 0 .. size) {
val item: T = T::class.java.getConstructor().newInstance()!!
method.invoke(item, this)
list.add(item)
}
return list
}

kotlin custom get immutable list from mutableList

I have a custom getter method for a mutable list to return an immtuable list by using Google's Guava library. And then this mutable list is accessed in the constructor.
data class mutableClass(val list: List<Foo>) {
private val mutableList: MutableList<Foo>
get() = ImmutableList.copyOf(field)
init {
mutableList = mutableListOf()
list.forEach {
mutableList.add(it.copy()) // Exception is thrown here.
// It actually calls its getter method which is an immutable
// list, so when init this class, it throw exception
}
}
}
data class Foo {}
And I decompile it to Java, in the init block, it calls the getter method of mutableList.
Is there a way to call the mutabbleList itself instead of getter method?
Of course it calls the getter (which returns ImmutableList.copyOf(field)).
You can do simply assignment to mutableList new copied mutable list in your init block:
data class MutableClass(val list: List<Foo>) {
private val mutableList: MutableList<Foo>
get() = ImmutableList.copyOf(field)
init {
mutableList = list.map { it.copy() }.toMutableList()
}
}
or whithout init:
data class MutableClass(val list: List<Foo>) {
private val mutableList: MutableList<Foo> = list.map { it.copy() }.toMutableList()
get() = ImmutableList.copyOf(field)
}
Kotlin stdlib opts for interface immutability. That means, the interface an implementation is boxed in determines the mutability of the reference itself.
Therefore, the right way to make a MutableList<T> just a List<T> is to box it, like follows:
val myMutableList = mutableListOf(1, 2, 3, 4)
val myImmutableList = myMutableList as List<Int>
That way, being the myImmutableList reference boxed in a List<Int>, it will only expose members from List<Int>, and not those that just MutableList<Int> define, which allow to mutate the state of the object, hence the list.
Then, if you really want to avoid the following issue (resuming from the above code),
val hackedList = myImmutableList as MutableList<Int>
... for which you would be able to access the mutable implementation through unboxing, you may rather be opting for the following solution:
class ImmutableList<T>(list: MutableList<T>) : List<T> by list
fun <T> MutableList<T>.toImmutable() = ImmutableList(this)
And then use it as follows:
val myMutableList = mutableListOf(1, 2, 3, 4)
val myImmutableList = myMutableList.toImmutable()
So you'll be avoiding the issue above. Indeed, any attempt to unbox the value return from MutableList<T>.toImmutable() will end up with a TypeCastException, as the implementation of the List<T> is no longer a MutableList<T>. Rather, it is an ImmutableList<T>, which doesn't expose any methods that might mutate the object.
Unlike #Lucas method, this way you won't be wasting time to copy elements, as you'll be relying on the by keyword in Kotlin, which allows you to implement an interface through an already existing implementation. That is, the MutableList<T> you'll be passing to the constructor of ImmutableList<T>.
When I was researching about this topic, the best solution it just worked for me is just enforcing by contract. If you are creating a mutable list, let's say:
val immutableList = mutableListOf(
Randomy.One,
Randomy.Two,
Randomy.Three
).toList() // We make it immutable?
and then you use an extension function or any of the recommendations given below, like using ImmutableList.copyOf(field), you might be paying a penalty because you're copying the items into another collection.
Another option is just paying the unboxing cost of doing something like:
val myImmutableList = myMutableList as List<Int>
The solution I opted for is just enforcing by contract, it's a very simple concept. Your MutableList inherits from List. If you want to share a collection of items with that level of abstraction, it's your choice to do it by enforcing the type:
val immutableList: List<Randomy> = mutableListOf(
Randomy.One,
Randomy.Two,
Randomy.Three
)
now if we share that list with another component, we'll use the right abstraction without any cost. We could also have used a Collection, because List inherits from Collection:
val immutableList: Collection<Randomy> = mutableListOf(
Randomy.One,
Randomy.Two,
Randomy.Three
)
For me, using a var instead of val field along with a private setter usually works best
class Order
class Something() {
var orders: List<Order> = listOf()
private set
fun addOrder(order: Order) {
orders = orders
.toMutableList()
.apply { add(order) }
}
}
This exposes it as immutable and requires a single field only. The price we pay is the overhead of creating a new collection when adding elements it.

Can we achieve compile-time type safety for a union of types we can't control?

Let's say I have a function:
fun doSomething(vararg pairs: Pair<String, *>) {
// Do things with the pairs
}
The problem with this approach is that it allows any type for the second half of Pair (e.g. Pair<String, CustomType1>).
What if I only want to allow a finite number of types, how would I achieve that?
If the function had a simpler signature, I could achieve the restriction via overload, like so:
fun doSomethingSimpler(param: Boolean) {
// Boolean implementation
}
fun doSomethingSimpler(param: Int) {
// Int implementation
}
// etc.
If the restricted type "set" was in my control, I could use an interface or a sealed class to achieve this. E.g.
sealed class Root
class Child1 : Root()
class Child2 : Root()
fun doSomethingICanControl(param: Root) {
// Root implementation
}
Yet what if I don't have control over the types or they are primitive, how do I prevent * from allowing everything through?
I know I could use smart-casts to get run-time safety, but can this be done at compile time?
Or does the language disallow it?
Edit 1
I know I could create my own box types (e.g. MyBoolean) and use a common interface or sealed class, but that would be boilerplate that everyone would have to write every time they needed to.
Edit 2
To be clear, I'd like to be able to make an invocation like so:
doSomething(
"key1" to false,
"key2" to "value2",
"key3" to 86
)
... I.e. Have a mixed set of "second" (of Pair) types.
So to sum it quickly up:
You want to call methods from a library that expects Pair<String, *>,
but limit the possible values that * can be.
TL;DR: What you are trying to accomplish is not possible without some kind of wrapper, because
We have no Sum-Types in Kotlin, so no way to tell the compiler that you expect an Int or a Double or a Float and nothing else
If a library-method expects something to be Pair<String, *>, there is no way for us to tell the compiler, that we just want to be able to give it a String instead of *
One way to get that behaviour is to create a Decorator (Decorator Pattern), e.g. create your own extension methods that allow only a subset
class Foo {
//Allows everything
fun doSomething(param: Pair<String, *>)
}
//Now lets create our own extension methods
fun Foo.doSomethingWithInt(param: Pair<String, Int>)
fun Foo.doSomethingWithBoolean(param: Pair<String, Boolean>)
fun Foo.doSomethingWithString(param: Pair<String, String>)
Or if you dont want to be able to call Foo.doSomething() you
can create a Decoractor-Class:
class FooDecorator {
val foo = Foo()
fun doSomething(param: Pair<String, Int>) { }
}
And the following example is not possible without some kind of Wrapper, because there are no Sum-Types in Kotlin:
doSomething(
"key1" to false,
"key2" to "value2",
"key3" to 86
)
What you could do is something like:
At first, create your own JSONItem type and add Extension-Methods to Types that can be used as one
class JSONItem<T> private constructor (item: T)
fun Int.asJSONItem() = JSONItem(this)
fun String.asJSONItem() = JSONItem(this)
fun Boolean.asJSONItem() = JSONItem(this)
Then you are able to do something like that:
//Your own personal doSomething
fun doSomething(varargs: param: Pair<String, JSONItem>) {
//Call the real doSomething()
doSomething(param.map { Pair(it.first, it.second.item) }}
}
doSomething(
"key1" to false.asJSONItem(),
"key2" to "value2".asJSONItem(),
"key3" to 86.asJSONItem()
)
Denotable union and intersection types are not currently supported in Kotlin (as of 1.1.x).
This is the relevant issue.

How to get generic param class in Kotlin?

I need to be able to tell the generic type of kotlin collection at runtime. How can I do it?
val list1 = listOf("my", "list")
val list2 = listOf(1, 2, 3)
val list3 = listOf<Double>()
/* ... */
when(list.genericType()) {
is String -> handleString(list)
is Int -> handleInt(list)
is Double -> handleDouble(list)
}
Kotlin generics share Java's characteristic of being erased at compile time, so, at run time, those lists no longer carry the necessary information to do what you're asking. The exception to this is if you write an inline function, using reified types. For example this would work:
inline fun <reified T> handleList(l: List<T>) {
when (T::class) {
Int::class -> handleInt(l)
Double::class -> handleDouble(l)
String::class -> handleString(l)
}
}
fun main() {
handleList(mutableListOf(1,2,3))
}
Inline functions get expanded at every call site, though, and mess with your stack traces, so you should use them sparingly.
Depending on what you're trying to achieve, though, there's some alternatives. You can achieve something similar at the element level with sealed classes:
sealed class ElementType {
class DoubleElement(val x: Double) : ElementType()
class StringElement(val s: String) : ElementType()
class IntElement(val i: Int) : ElementType()
}
fun handleList(l: List<ElementType>) {
l.forEach {
when (it) {
is ElementType.DoubleElement -> handleDouble(it.x)
is ElementType.StringElement -> handleString(it.s)
is ElementType.IntElement -> handleInt(it.i)
}
}
}
You can use inline functions with reified type parameters to do that:
inline fun <reified T : Any> classOfList(list: List<T>) = T::class
(runnable demo, including how to check the type in a when statement)
This solution is limited to the cases where the actual type argument for T is known at compile time, because inline functions are transformed at compile time, and the compiler substitutes their reified type parameters with the real type at each call site.
On JVM, the type arguments of generic classes are erased at runtime, and there is basically no way to retrieve them from an arbitrary List<T> (e.g. a list passed into a non-inline function as List<T> -- T is not known at compile-time for each call and is erased at runtime)
If you need more control over the reified type parameter inside the function, you might find this Q&A useful.