54

I know that data class are like simple models in kotlin with getters and setter by default and are as simple this:

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

Is it possible to declare a second constructor for that data class?

Kong
  • 7,648
  • 14
  • 58
  • 89
Eder Padilla
  • 1,067
  • 1
  • 9
  • 17

10 Answers10

41

A Kotlin data class must have a primary constructor that defines at least one member. Other than that, you can add secondary constructors as explained in Classes and Inheritance - Secondary Constructors.

For your class, and example secondary constructor:

data class User(val name: String, val age: Int) {
    constructor(name: String): this(name, -1) { ... }
}

Notice that the secondary constructor must delegate to the primary constructor in its definition.

Although many things common to secondary constructors can be solved by having default values for the parameters. In the case above, you could simplify to:

data class User(val name: String, val age: Int = -1) 

If calling these from Java, you should read the Java interop - Java calling Kotlin documentation on how to generate overloads, and maybe sometimes the NoArg Compiler Plugin for other special cases.

Jayson Minard
  • 74,658
  • 30
  • 167
  • 210
18

Yes, but each variable should be initialized, so you may set default arguments in your data class constructor, like this:

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

Now you can create instance of this data class in two ways

  • Person(30)
  • Person(20, "Bob")
Alexey Denysenko
  • 3,460
  • 1
  • 15
  • 26
14

Updated answer for data classes:

Yes you can, but you will need to delegate everything to the primary constructor

data class User(val name: String, val age: Int)
{
    constructor(name: String): this(name, -1) {
    }

    constructor(age: Int): this("Anon", age) {
    }
}

// Anon name: Anon
println("Anon name: " + User(30).name)

// No age: -1
println("No age: " + User("Name").age)

// Name: Name age: 20
val u = User("Name", 20)
println("Name: " + u.name + " age: " + u.age)

You can also set default values in your primary constructor as Alexey did.

Robert
  • 4,840
  • 1
  • 24
  • 29
  • In Data class we can't have empty constructor. We should have min one param. – Muthukrishnan Rajendran Jun 06 '17 at 13:56
  • Ah sorry, misread data class, in that case this answers your question: https://stackoverflow.com/questions/37873995/how-to-create-empty-constructor-for-data-class-in-kotlin-android. – Robert Jun 06 '17 at 14:54
  • @Robert what does an empty constructor have to do with the question. You can easily just fix your answer here to be for data classes. – Jayson Minard Jun 06 '17 at 18:45
11

Default values in the primary constructor eliminates many needs for secondary constructors, but if the needed instance depends on logic based on data that must be analyzed the better answer may be to use a companion object.

data class KeyTag(val a: String, val b: Int, val c: Double) {
    companion object Factory {
        val empty = KeyTag("", 0, 0.0)

        fun create(bigString: String): KeyTag {
            // Logic to extract appropriate values for arguments a, b, c
            return KeyTag(a, b, c)
        }

        fun bake(i: Int): KeyTag = KeyTag("$i", i, i.toDouble())
    }
}

Usage is then:

val ks = KeyTag.create("abc:1:10.0")
val ke = KeyTag.empty
val kb = KeyTag.bake(2)
Mike Hanafey
  • 5,276
  • 3
  • 15
  • 25
  • 1
    This is a great solution, and I need it for exactly the reason you mentioned: I need to do some calculations/conversions before I can call the constructor and I'd rather include that logic with the data class and not in some unrelated file. – Zonker.in.Geneva Apr 18 '20 at 20:15
9

Data class will ensure consistency and meaningful behavior also we need to have val for immutability.

data class SampleData(val name: String, val age: Int, val email: String ?= null) {
constructor(name: String, age: Int) : this(name, age, null) {

    }
}

secondary constructor must delegate to the primary constructor in its definition, so to maintain the immutability, having "null" will work.

Taslim Oseni
  • 4,610
  • 10
  • 34
  • 50
boopathiraja
  • 171
  • 4
  • 9
  • 1
    In your example, the secondary constructor does not add anything. The primary constructor can already be called with only 2 parameters (due to the default value). – charles-allen Jun 23 '19 at 14:42
8

you can set the data class like this

data class User(val name: String? = null, val id: String? = null, val email: String? = null)

and you can instance the object with multiple constructors like this

val userId = User(id = "123456")
        val userMail = User(email= "email@email.com")
        val userName = User("Name")
Eder Padilla
  • 1,067
  • 1
  • 9
  • 17
6

I wanted to have a class similar to below (with a constructor that parses an input)

data class Data(val a: String, val b: String) {
    constructor(spaceSeparated: String) { // error because we don't call this()
        val split = spaceSeparated.split(" ")
        this(split.first(), split.last()) // error again because it's not valid there
    }
}

The solution is to do this:

data class Data(val a: String, val b: String) {
    companion object {
        operator fun invoke(spaceSeparated: String): Data {
            val split = spaceSeparated.split(" ")
            return Data(split.first(), split.last())
        }
    }
}

And it can be called just as if it were a constructor

Hack5
  • 1,889
  • 15
  • 24
4

Yes, we can use like below code, and in primary constructor for data class should have min one parameter.

data class SampleData(val name: String, val age: Int) {
    constructor(name: String, age: Int, email: String) : this(name, age) {

    }
}
Muthukrishnan Rajendran
  • 10,023
  • 2
  • 25
  • 37
3

Instructs the Kotlin compiler to generate overloads for this function that substitute default parameter values. If a method has N parameters and M of which have default values, M overloads are generated: the first one takes N-1 parameters (all but the last one that takes a default value), the second takes N-2 parameters, and so on.

 data class User @JvmOverloads  constructor(
var email: String="",
var password: String="")
RanaUmer
  • 311
  • 2
  • 5
0

Yes you can have multiple contractors on data classes. But there is something which makes the primary constructor special. The compiler will auto generate methods like equals, hashCode, copy, toStrings based on the primary constructor for the data class.

Below is an example of two instances of a data class which looks different (first.gender = male, second.gender = female) but equals method would evaluate to true because gender is not defined in the primary constructor and therefore not considered in the auto generated methods. Likewise, gender would not be included in the string representation.

data class A(val name: String, val age: Int) {
    var gender: String = "Female"
    constructor(name: String, age: Int, gender: String) : this(name, age) {
        this.gender = gender
    }
}

fun main(args: Array<String>) {
    val first = A("foo", 10)
    val second = A("foo", 10, "Male")
    println(first == second)  //prints true
    println(second)  //prints A(name=foo,age=10)
}
furkan
  • 253
  • 2
  • 9