3

I am reading the book Kotlin in action and I ask myself what is the purpose of "creating an instance of a class using a constructor reference" (page 112 if anyone is interested and has the book at home).

Here is the code example from the book:

data class Person(val name: String, val age: Int)

val createPerson = ::Person
val p = createPerson("Alice", 29)

println(p) // Person(name=Alice, age=29)

I think it looks like an factory method call, but I dont think that this is the (only) purpose of the method reference here.

Lino
  • 19,604
  • 6
  • 47
  • 65
Torben G
  • 750
  • 2
  • 11
  • 33

2 Answers2

4

A constructor when referenced this way is just like any other function reference. It has inputs (the parameters) and a return value (a new instance of the class). You can pass it to a higher-order function that has a function parameter or some kind of factory.

For example:

class MessageWrapper(val message: String)

val someStrings = listOf("Hello world")

You could convert your list to have the wrapper type like this by using a lambda:

val someMessages: List<MessageWrapper> = someStrings.map { MessageWrapper(it) }

but it is possibly clearer to skip wrapping your function in another function by passing the constructor directly.

val someMessages: List<MessageWrapper> = someStrings.map(::MessageWrapper)

The improvement in clarity is more apparent with functions and parameters than it is with constructors, though. It also can help avoid shadowed its by avoiding nested lambdas.

Tenfour04
  • 83,111
  • 11
  • 94
  • 154
  • 1
    It isn't going to be more efficient (or _very_ slightly so); in the `map` case they are completely equivalent after inlining, for a non-inline function both need to create an object. – Alexey Romanov Feb 02 '20 at 21:03
  • Yeah, it’s the wrong word to use here. What I’m trying to say is that it more directly communicates intent. – Tenfour04 Feb 02 '20 at 21:20
  • Okay so the benefit is conciseness but it isn't more efficient because its the same under the hood / after compiling? – Torben G Feb 05 '20 at 04:34
  • After compiling it will be essentially the same. If inlined, the function is called directly. If not inlined, you either have a lambda function object that calls the function, or you have an object wrapper around the function call. Actually I think they would compile exactly the same way. Aside from conciseness, it makes the code easier to read, especially when the function has parameters. This applies more to functions and properties rather than constructors. For example, `someList.sortBy(MyObject::name)` vs. `someList.sortBy { it.name }`. It’s actually longer but has clearer intent. – Tenfour04 Feb 05 '20 at 05:48
  • And it’s less cognitive load to read than `someList.sortBy { myObject -> myObject.name }`. – Tenfour04 Feb 05 '20 at 05:50
1

References to constructors are the part of Kotlin Reflection API. You can create an instance of a class through constructor reference even if that class is not even in your project (you get that reference from outside). Reflection is widely used by many libraries and frameworks (for example, GSON) that know nothing about your code but still are able to create instances of your classes.

ardenit
  • 3,610
  • 8
  • 16