3

I am unsure if it is possible for a kotlin extension to be set like a java object.

In my program I have a java class called Submission and I wanted to create a kotlin extension of that called categories - an ArrayList - so I made it like this.

var Submission.categories: ArrayList<String>
    get() {
        return this.categories
    }
    set(categories){
        this.categories = categories
    }

However whenever I try set a category the program just crashes with a stackOverflowError like this:

ERR: stack=java.lang.StackOverflowError: stack size 8MB
             at com.....setCategories(Extensions.kt:0)
             at com.....setCategories(Extensions.kt:19)
             at com.....setCategories(Extensions.kt:19)
             at com.....setCategories(Extensions.kt:19)
             at com.....setCategories(Extensions.kt:19)
             at com.....setCategories(Extensions.kt:19)

This seems like the right syntax for declaring kotlin extensions. So I am really unsure of what direction I ought to go about fixing this in. Perhaps I should really just be using plain old inheritance?

Thanks.

Kirill Rakhman
  • 42,195
  • 18
  • 124
  • 148
nmu
  • 1,442
  • 2
  • 20
  • 40
  • You're not defining any custom logic in getters in setters, so why do you need them? The issue is caused by incorrect setter declaration, `this.categories = categories` will call the `set()` method, which will result in an endless loop. – Egor Dec 19 '16 at 12:50
  • @Egor Ah that makes perfect sense. The reason I defined the get and set methods is because it refuses to compile without a get method and return statement. I assumed it was the same for a set as well and did that too. I'm going to try remove the set logic and see what happens. – nmu Dec 19 '16 at 13:40

1 Answers1

4

Your code throws a StackOverflowError because it calls itself recursively. this.categories just calls the getter it is already in and this.categories = categories calls the setter it is already in.

Extension properties are only syntactic sugar for a pair of (extension) getters and setters. Inside the property accessors (i.e. the getter and the setter) you can only access what is already publicly accessible from the receiver class. If it has public fields, you can access them, however you can't define new fields. If you want to save additional state but the class has no API for that, you're out of luck.

What a mutable extension property typically does is redirecting to existing mutating functions. Here's an example.

//java
class Foo {
    private List<String> items;

    public String myItems() {
        return items;
    }
}


//kotlin
var Foo.firstItem: String
    get() = myItems()[0]
    set(value) {
        myItems()[0] = value
    }
Kirill Rakhman
  • 42,195
  • 18
  • 124
  • 148
  • Even after modifying my getters and setters to avoid the recursive call - as you have pointed out. The stackOverflowError just moves to the get(). However, If I understand your post correctly this shouldn't work anyway because `categories` isn't a public field in this program, I was trying to define a new field. So I guess that's the root problem. – nmu Dec 19 '16 at 13:58
  • 4
    I totally agree with this answer, but it might be worth mentioning that, while extensions cannot modify the class to add backing fields, additional state still can be stored somewhere outside the class, and delegates can help here. For more details on this workaround, see: http://stackoverflow.com/a/36511438/2196460 – hotkey Dec 19 '16 at 13:59
  • @hotkey thanks for the link. To be sure, would this work with java classes as well – nmu Dec 19 '16 at 17:15
  • 1
    @nmu, yes, it will, you can define Kotlin extensions for Java classes as well. If you need to **use** these extensions in Java, they are visible as static functions. For example, a `tag` extension defined for `MyClass` in `KotlinFile.kt` will be seen as `KotlinFileKt.getTag(MyClass $receiver)` and a similar setter. – hotkey Dec 19 '16 at 17:27
  • @hotkey Ah, perfect. Going to try this now. Appreciate the help – nmu Dec 19 '16 at 17:49
  • 1
    The code snippet in my answer actually features a Java class. – Kirill Rakhman Dec 19 '16 at 19:57