hello there, very new to Kotlin (as in, I've start...
# getting-started
j
hello there, very new to Kotlin (as in, I've started today), and I'm trying to find the way to have a secondary constructor work out in the following scenario: consider a chess board position that can be represented either through (x, y) coordinates (as a multidimensional array would), or as a string such as "B4"
Copy code
class Position(val x: Int, val y: Int) {
	constructor(positionCode: String) {
		// construct a position from code here
	}
}
this yields an error as the secondary constructor must call the primary one with
: this(...)
i don't see an easy way to achieve this, i could call a function in the parent call such as
this(getCoordsFromCode(positionFromCode))
but it obviously won't work as the primary one expects two ints, and I don't think you can unpack an array into a function that isn't expecting vararg i could obviously swap the constructors and have the parent constructor call be something like
this(getCodeFromCoords(x, y))
, but I was wondering if I was missing something (and I'm pretty sure I am)
m
You could use a
companion object
and a factory function:
Copy code
class Position(val x: Int, val y: Int) {
    companion object {
        fun fromCode(positionCode: String): Position {
            // construct a position from code here
        }
    }
}
You can then call
Position.fromCode()
to create a
Position
given a position code.
a
Or write the factory function like an "extension constructor." If you declare a top-level function with the same name as the class, you can invoke it with the same syntax as a constructor call:
Copy code
fun Position(positionCode: String): Position {
  // parse the position code
  return Position(x, y)
}

class Position(val x: Int, val y: Int) {
  // other class contents
}
and also avoid taking dependencies on unrelated "constructor" parameter types or parsing formats in a class's core implementation
j
@Mark Murphy exactly what I ended up doing, but was still curious if there was a way with pure constructor overloading @Adam Powell oh that's pretty nifty, is that considered good practice? no particular downsides to it?
a
It's used in a number of libraries like
kotlinx.coroutines
for
Job()
j
good to know! thanks to you both
a
the downsides to it are that sometimes there are better/more idiomatic solutions in particular situations
m
for the pure-constructor approach, I think that you would need to use
this(getXFromCode(positionCode), getYFromCode(positionCode))
, requiring two parsing passes, which isn't ideal
a
if the parameters don't vary by type you'll probably want something with a more descriptive name. Regular top-level factory functions work well there, but compete with the companion object style in terms of discoverability - java developers are used to typing
Typename.
to get an autocomplete list of options.
see things like
listOf()
for an example of that
and if you're taking only a single parameter that you're converting from or decorating,
fun Foo.toBar(): Bar
or
fun Foo.asBar(): Bar
is more idiomatic, respectively
and yeah to @Mark Murphy’s point, if you want a primary constructor you're left double-parsing or alternatively, declaring all of your constructors as secondary constructors java-style
but kotlin in general strongly encourages small classes and decoupled extensions written to classes' public api surface
j
@Adam Powell can you elaborate on "if the parameters don't vary by type you'll probably want something with a more descriptive name" -> what do you mean by "don't vary by type?" also, shouldn't the autocomplete still discover companion object's methods even if with a toplevel factory function defined?
a
If you have a top level factory function it by definition isn't part of the companion object to find it there 🙂
and if you had two different string formats to parse you'd have two functions that each take a string as a parameter, and they'd have to have different names. You couldn't just use the class name and treat them like constructors. Same problem you'd have with normal constructors too.
j
oh, sure but i mean, in the current example, if I type
Position.
, I'm not expecting to find a top level function as a suggestion
a
developers new to kotlin sometimes don't think to look for top-level functions or
.toFoo()
extensions; it's temporary as an audience learns the language but something to occasionally consider.
j
right thanks again for all this valuable input