https://kotlinlang.org logo
Title
s

Seri

08/14/2019, 10:13 PM
Can a constructor be written as an extension method?
c

Casey Brooks

08/14/2019, 10:23 PM
You can make a factory function on a class’s companion object, if it exists. The semantics of it are a bit strange and can be pretty unintuitive, but it can be done. For example:
operator fun String.Companion.invoke(any: Int) : String {
    return any.toString()
}

fun callStringFactory() {
    val s: String = String(123)
}
s

Seri

08/14/2019, 10:25 PM
Ah awesome, I had stumbled onto something like that online
I’m having trouble doing the same to a generic class, though
c

Casey Brooks

08/14/2019, 10:30 PM
I don’t think you’ll be able to make a generic function out of it, since not all classes have companion objects, and there’s no way to declare an interface or anything on a class to mandate that it has one
s

Seri

08/14/2019, 10:31 PM
Right, I was trying to get around it by using interfaces, but it wasn’t accessible, like you mentioned
Maybe I should say what I’m trying to accomplish instead! I’m trying to create a DSL that can take any sealed class as a generic property, then operate on it using it’s invoke function.
sealed class Model {
  class A: Model()
  class B: Model()
}

class Dsl<T>()

fun <T> dsl(init: Dsl<T>.() -> Unit): Dsl<T> {
  // DSL boilerplate
}

val test = dsl<Model> {
  Model.A {
    // Do stuff
  }
  Model.B {
    // Do stuff
  }
}
c

Casey Brooks

08/14/2019, 10:41 PM
You might go with an approach like Ktor, where you declare an interface on the companion object itself. And then the DSL function can look for that interface
interface DslCompanionInterface

sealed class Model {
    class A: Model() {
        companion object : DslCompanionInterface
    }
    class B: Model() {
        companion object : DslCompanionInterface
    }
}

class Dsl<T>() {
    operator fun T.invoke(block: ()->Unit) {

    }
}

fun <T : DslCompanionInterface> dsl(init: Dsl<T>.() -> Unit): Dsl<T> {
    // DSL boilerplate
}

val test = dsl<DslCompanionInterface> {
    Model.A {
        // Do stuff
    }
    Model.B {
        // Do stuff
    }
}
s

Seri

08/14/2019, 10:43 PM
That looks pretty promising! I’ll give that approach a shot and play around with it
It looks like it may be more promising to just require that the parent sealed class be composed of objects, and to figure out another solution for State. Thanks!