Overload constructor


#1

Hey all! Just something I ran into that isn’t a super big deal but I thought would be interesting.

One common implementation pattern is to use a static factory method to wrap and potentially avoid construction calls:

class Resource(val value: Int)
val zero = Resource(0)
fun newResource(val value: Int) = if (value == 0) zero else Resource(value)

Now this factory method is necessary in cases where you plan to use a mechanism like that shown above to intern, pool, etc. But in the simplest cases where you aren’t doing that, the factory method is extraneous. However, when I’m building new code, I’m always trying to decide whether to include the wrapper method in the public API, so I can introduce optimizations like this without changing the API for downstream projects.

Idea: allow top-level functions to shadow constructors in the same namespace.

class Resource(val value: Int)
val zero = Resource(0)
fun Resource(value: Int) = if (value == 0) zero else ::Resource(value)

The nice thing here is that I get to follow KISS method at first (only include line #1); and later, if and when the optimization becomes relevant, I can transparently introduce it (as long as downstream code isn’t taking the constructor by reference, which I think would be the less common usage scenario). In some ways it’s similar to what you might do by overloading new for a class in C++.

The general use case is, any time you want to be able to avoid object instantiations after the fact, without modifying your API.

Thoughts?


#2

This reminds me of using a companion object and the invoke operator:

fun main(args: Array<String>) {
    val r = Resource(5)
}

class Resource private constructor(val value: Int) {
    init {
        println("Init")
    }
    
    companion object {
        operator fun invoke(value: Int) {
            println("COMPANION")
        }
    }
}

An alternative is to declare an extension function on the companion object:

fun main(args: Array<String>) {
    val r = Resource(5)
}

class Resource private constructor(val value: Int) {
    init {
        println("Init")
    }
    companion object
}

operator fun Resource.Companion.invoke(value: Int) {
    println("COMPANION")
}

Unlike your suggestion, doing it this way requires making the constructor private (or make sure the constructor and the factory extension method has a different signature). The reason for this is that the class constructor automatically beats (or shadows) the companion factory.


#3

Aha! OK, that more or less takes care of my use case. Not quite as clean as I had hoped but still pretty decent, for something that won’t necessarily come up super often.

Thank you!