https://kotlinlang.org logo
Title
d

dierre

04/13/2018, 2:18 PM
Hello, I have an interoperability design issue that you can help me with. I’m really fond of kotlin DSL so I did something like this:
class Car (
        val model: String?,
        val year: Int,
        val required: String
) {

    private constructor(builder: Builder) : this(builder.model, builder.year, builder.required)

    companion object {
        inline fun build(required: String, block: Builder.() -> Unit) = Builder(required).apply(block).build()
    }

    class Builder(
            val required: String
    ) {
        var model: String? = null
        var year: Int = 0

        fun build() = Car(this)
    }
}
so that in kotlin I can do
val car = Car.build(required = "") {
        model = "X"
        year = 120
    }
The problem is that in Java then I need to write:
Car.Builder builder = new Car.Builder("required");
        builder.setModel("X");
        builder.build();
to do the same stuff. This is not really readable. So I did something this:
class CarJavaFriendly(private val model: String?, private val year: Int) {
    
    private lateinit var required: String
    
    fun withRequired(b: String) = this.apply { required = b }

    fun execute() {
        tryExecute { someFunction() } }
    }

    private fun <B> tryExecute(f: () -> B): B = try {
        f()
    } catch (e: UninitializedPropertyAccessException) {
        val msg = e.message?.replace("lateinit property ", "") ?: "a property has not been initialized"
        throw ValueRequiredException(msg)
    }
}
so that in java you can write
CarJavaFriendly carJavaFriendly =
                new CarJavaFriendly("X", 1)
                        .withRequired("x")
                        .execute(somefunction());
so that I can exploit lateInit to check if a parameter which is required, it’s actually initialized. Do you think is there a better approach when it comes to writing API which are readable for Java as well?
k

kingsley

04/15/2018, 3:09 PM
By the way, if you were using >= Java 8, you could write something close to:
Car car = Car.build("required", (b) -> {
  b.model = "X";
  b.year = 120;
});