Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Local parameters in Kotlin's primary constructor of a data class

Tags:

Regarding data classes it is forbidden to not use var or val keywords in the primary constructor, i.e. every parameter is implicitly turned into a class property. However, sometimes there are cases which I don't want each parameter to be turned into a class property.

So, as far as I can see, there is no chance of passing a parameter in a primary constructor that is accessible only within the constructor and is forgotten after the construction of the instance has finished. Is there a good reason for this?

The only way I see to get around this, is not to use data classes or to use a secondary constructor that allows for non-var/val-prefixed variables. However, having a lot of parameters that need to be passed, a secondary constructor would immensely inflate the class. Of course, I could wrap all the parameters into another object, but that would just kind of shift the problem to another place.

Is there a recommended approach or pattern in order to cope with that?

like image 284
Jan B. Avatar asked Jul 29 '18 13:07

Jan B.


People also ask

What is primary constructor?

Constructors The primary constructor is a part of the class header, and it goes after the class name and optional type parameters. class Person constructor(firstName: String) { /*...*/ }

How is primary constructor different from secondary constructor?

The primary constructor initializes the class, while the secondary constructor is used to initialize the class and introduce some extra logic.

Can data class have secondary constructor?

Note that it's compulsory to have a primary constructor in a data class. It's also compulsory to have the val or var keyword before the variable name, which you can get away with in normal classes and secondary constructors.


1 Answers

You are not limited at all, you just have to do things a bit differently.

Data classes are intended to be very clear about what they contain and in what order, and only allow members in the primary constructor parameter list.

But you have other options: use a secondary constructor, and/or create top-level functions with the same name as the class with different overloads, or create factory methods in the companion object:

data class Person(val name: String, val age: Int) {
    // secondary constructor
    constructor (name: String): this(name, 0) {
       // ... make a newborn
    }

    // factory methods in companion object
    companion object {
        fun of(name: String, birthdate: LocalDate): Person {
            return Person(name, yearsSince(birthdate))
        }
    }
}

// function with same name as class acting like a constructor
fun Person(name: String, birthdate: LocalDate): Person {
    return Person(name, yearsSince(birthdate))
}

// these all work now:

Person("Fred", 30)                                  // primary constructor
Person("Baby")                                      // secondary constructor
Person("Geoff", LocalDate.parse("12/08/1990"))      // top-level function
Person.of("Jennifer", LocalDate.parse("01/01/1981") // companion function

You can also hide the primary constructor by making it private, but you cannot hide the copy version of that constructor.

By the way, having data classes with this contract for the primary constructor really help serialization/deserialization libraries know what to do with the class that would be guesswork otherwise. It is a good thing!

like image 106
Jayson Minard Avatar answered Sep 28 '22 17:09

Jayson Minard