user38725
user38725

Reputation: 903

Kotlin type-safe builders

I use the Kotlin "type-safe builder" pattern in my code:

insteadOf("search", "forest|ground") {
    println("Do something.");
}

However, I am also using Kryo to serialize data (save a game), and Kryo doesn't like the anonymous type:

call: () -> Unit

So I had to introduce a "Do"-interface instead of the above type. But now my code looks a lot uglier:

insteadOf("search", "forest|ground", object : World.InsteadOf.Do {
     override fun invoke() {
          println("Do something")
     }
})

How can I use the simple { } - syntax with the Do-interface?

EDIT:

Here's some clarification.

I'm kind of confused (not news).

The actual Kryo error message seems to be about no-args constructor, however instantly after that it says: "This is an anonymous class, which is not serializable by default in Kryo.".

Here is the full error message:

Exception in thread "main" com.esotericsoftware.kryo.KryoException: Class cannot be created (missing no-arg constructor): fi.starstuff.rogue.World$insteadOf$1
    This is an anonymous class, which is not serializable by default in Kryo. Possible solutions: 1. Remove uses of anonymous classes, including double brace initialization, from the containing class. This is the safest solution, as anonymous classes don't have predictable names for serialization.
    2. Register a FieldSerializer for the containing class and call FieldSerializer#setIgnoreSyntheticFields(false) on it. This is not safe but may be sufficient temporarily. Use at your own risk.
Serialization trace:
call (com.mygame.World$InsteadOf)
insteadOfs (com.mygame.World)
    at com.esotericsoftware.kryo.Kryo$DefaultInstantiatorStrategy.newInstantiatorOf(Kryo.java:1319)
    at com.esotericsoftware.kryo.Kryo.newInstantiator(Kryo.java:1127)
    at com.esotericsoftware.kryo.Kryo.newInstance(Kryo.java:1136)

Here's the InsteadOf-class:

class InsteadOf {
        var verbs: Array<String> = arrayOf()
        var nouns: Array<String> = arrayOf()
        var where: String = ""
        var call: (() -> Unit)? = null
    }

Upvotes: 2

Views: 744

Answers (3)

user38725
user38725

Reputation: 903

Well, I simply ended up switching to core Java serialization and the problem went away.

Upvotes: 0

Sergei Rybalkin
Sergei Rybalkin

Reputation: 3453

It's not completely clear what's wrong with your code. But if you are missing a no-args constructor there are two ways:

  1. Obviously add it
  2. use no-arg compiler plugin to specify class that need extra constructor org.jetbrains.kotlin:kotlin-noarg

A lot of serialisation-related libs force user to have no-arg constructor.' There is a related issue in kryo github.

In case your problem is not related to no-arg - your question will be a duplicate for this

Upvotes: 0

hotkey
hotkey

Reputation: 148159

You can basically accept a () -> Unit function in insteadOf and wrap it into a Do instance in the body:

fun insteadOf(foo: String, bar: String, call: () -> Unit) { 
    val doInstance = World.InsteadOf.Do(call)
    /* use doInstance */
}

The usages will stay the same as in your first example.

Alternatively, provide the Do interface with a factory function that accepts () -> Unit and returns a Do instance.

Upvotes: 1

Related Questions