something am thinking about lately. would you use ...
# getting-started
m
something am thinking about lately. would you use factory methods on a class, for example
create()
, method that is explicitly named or use
invoke
operator function on a
companion object
?
v
iirc you can have a factory method on the companion object that is named like the class
a
I usually reach for factory functions outside of the class entirely, at the top level, named the same as the class
m
thanks
d
@Adam Powell, any advantages over companion objects, or just your personal favourite way?
In the case of a private constructor, the only way would be to use `companion object`s anyways... and factories are usually there to replace constructors that might not be as safe/flexible... no?
a
It shares the same abstraction layering benefits as extension functions: it gives identical call site appearance and discoverability as a constructor, but restricts the implementation to only use the public API of the class. If you need private access then the factory also becomes responsible for creating a valid and internally consistent instance.
If the process of the factory creating an instance involves side effects then I prefer named functions that call attention to those side effects instead
d
but restricts the implementation to only use the public API of the class
For that, you'd need to put the actual implementation in a different module as
internal
... unfortunately Kotlin doesn't yet have package protected classes... whereas:
Copy code
data class Foo private constructor(val baz: String) {
   companion object { operator fun invoke(...) {... return foo }
}
effectively "hides" the constructor even in the same module... unless I'm missing something?
a
No
internal
implied above; the idea is to keep the factory implementation "honest" - public API access only, such that anyone in another module could write it if they were so inclined
The simplest and best way to construct an object absent any other complications or constraints is a constructor. Everything else is trying to compete with
TypeName(...)
in terms of usability.
It's best for constructors to not have side effects visible outside of the object they are constructing. If creating an object has those kinds of side effects, a named factory method on the companion or elsewhere can make those side effects more clear
For most other convenience purposes, the top-level function named after the class most closely appears and behaves like a constructor, and by explicitly not being part of the companion object you're forced to design the type itself with a public API suitable for this kind of usage and extension.
d
If you need private access then the factory also becomes responsible for creating a valid and internally consistent instance.
Ok, that's a great point! I finally understood what you meant there with the extra explanations... The real class's constructor should ensure validity/consistency... the factory methods are only for convenience like for using one type to create another (like translating classes from the data layer to the domain layer?). I guess when it comes to `value class`es though, this gets a bit complex... it's easier now with
init
blocks... but constructors can ONLY have one parameter and MUST be it's internal value... so I guess then, we wouldn't use a separate function, but rather a `companion object`'s
invoke
operator and hide the main constructor...