Given this code: ```data class Flow( val ids: ...
# announcements
i
Given this code:
Copy code
data class Flow(
    val ids: List<MyId>,
    val steps: MySteps
) {

    constructor(ids: List<MyId>) : this(
        ids,
        MySteps(ids.toSteps(), ids.toSteps().first())
    )
}

data class MySteps(val steps: List<MyStep>, val currentStep: MyStep)
How can I throw an error in the convenience constructor if
ids
is not empty (such that it crashes before
ds.toSteps().first()
?
w
Copy code
constructor(ids: List<MyId>) : this(
    ids.ifEmpty { error("Not possible") },
    MySteps(ids.toSteps(), ids.toSteps().first())
)
i
@wbertan awesome, thanks!
s
Avoid throwing exceptions during construction of an object. Consider creating a factory method/function instead that will either throw that exception or return an instance of that class if all input is correct. For a factory method to look like a constructor, add the
operator fun invoke(...): Flow { ... }
to the `Flow`'s companion object.
2
w
@streetsofboston OMG! That is amazing, I wasn’t aware of that! @iex
Copy code
companion object {
        operator fun invoke(ids: List<MyId>): Flow {
            if(ids.isEmpty()) {
                error("Not possible!")
            }
            return Flow(
                ids,
                MySteps(ids.toSteps(), ids.toSteps().first())
            )
        }
    }
💯 1
i
@streetsofboston thanks, aware of factory methods... But I don't see how this helps in my particular case.
s
Copy code
data class Flow private constructor(
    val ids: List<MyId>,
    val steps: MySteps
) {
    companion object {
        operator fun invoke(ids: List<MyId>) : Flow {
            if (ids.isEmpty()) error("....)"
            return Flow(ids, MySteps(ids.topSteps(), ids.toSteps().first())
        }
    }
}
m
Are there particular downsides to throwing an exception from the constructor? Or is it more of a style/expectations thing?
s
It is more like a style but you also avoid the construction of an object, allocation of memory, followed by throwing an exception which will cause the object to be GCed. Also, in the future you may want to return something else, not a
Flow
or throwing an exception, but maybe something more like a
Either<Error, Flow>
. A factory method can be changed to make that happen, a constructor can only return a Flow, never something else
i
avoid memory allocation + flexible return types
k
Or
Flow?
m
Thank you for the follow up. Firms up my suspicions. I've been adopting
Either
more and more as I like the separation of business errors from truly exceptional occurrences.