edrd
08/16/2020, 2:37 PMfun Class::function
syntax.
2. Automatically create companion objects when creating extension functions for classes.
---
Currently, defining extension functions for companion objects involves some boilerplate:
class Foo {
companion object
}
fun Foo.Companion.bar() = ...
For Java classes, it's not possible to define extension functions since there's no concept of companion objects.
However, it's possible to reference static functions:
fun parseInts(values: List<String>) = values.map(Integer::parseInt)
Since the ::
syntax is already used for referencing functions for companion, member and Java's static functions, it could also be used for defining extension functions:
fun List<String>::parseInts() = map(Integer::parseInt)
For Java, this would generate code similar to what is generated for extension functions of instances. For Kotlin, it would do two things:
• Create the companion object for the class being extended, unless it already exists.
(Current version requires declaring companion object
inside the class to be extended. Besides being confusing when the companion object is empty, it's not intuitive since there's no need for any special declarations when defining non-companion extension functions)
• Add the extension function to the companion object.
---
Primary use case would be for factory methods.
A real-world example using Exposed:
// commonMain/src/kotlin/User.kt
data class User(
val name: String,
val email: String
) {
companion object // <-- confusing
}
// jvmMain/src/kotlin/User.kt
fun User.Companion.fromResultRow(resultRow: ResultRow): User = ...
fun findUsers() = Users.selectAll().map(User::fromResultRow)
This would work because it's pure Kotlin, but it would require declaring companion object
only to allow adding extension functions. It doesn't express any intention and is inconsistent with the way non-companion extension functions work, as they require no additional declarations on the extended class. However, the main motivation of automatically creating companion objects is to hide this Kotlin-only detail when defining the extension function, allowing extension functions for Java classes.
With the proposal, it would look like this:
// commonMain/src/kotlin/User.kt
data class User(
val name: String,
val email: String
)
// jvmMain/src/kotlin/User.kt
fun User::fromResultRow(resultRow: ResultRow): User = ...
fun findUsers() = Users.selectAll().map(User::fromResultRow)
Which would also work if User
was a class witten in Java.
The alternative for Java today is to write top-level functions. A good example is jackson-module-kotlin, which adds a kotlinObjectMapper
factory function. While it works, using this approach for every DTO in a system would pollute the top-level namespace, making autocompletion less effective.
Another option would be to create a ResultRow.toUser
extension function instead, but that would also pollute the namespace of a very general class.