Question
How do you declare a secondary constructor in Kotlin, and where is the official documentation? Why does this class fail to compile?
class C(a: Int) {
// Intended secondary constructor
this(s: String) : this(s.length) {
// ...
}
}
Short Answer
You will learn the syntax for Kotlin secondary constructors, how they delegate to a primary constructor with this(...), and when a constructor body is useful. You will also see why a secondary constructor must begin with the constructor keyword.
Concept
A constructor creates and initializes an object.
Kotlin supports two kinds of constructors:
- A primary constructor, written in the class header.
- A secondary constructor, written inside the class body with the
constructorkeyword.
A class can have zero or one primary constructor and can have multiple secondary constructors. When a class has a primary constructor, every secondary constructor must delegate to it, directly or indirectly.
class C(val a: Int) {
constructor(s: String) : this(s.length)
}
Here, constructor(s: String) is the secondary constructor. The expression : this(s.length) calls the primary constructor, passing the length of s as its a argument.
This delegation rule matters because it guarantees that the primary initialization path runs before the secondary constructor body. It prevents an object from being only partially initialized.
Official Kotlin documentation covers this under Classes and Constructors, especially the section on secondary constructors.
Mental Model
Think of the primary constructor as the main entrance to a building.
A secondary constructor is another entrance, such as a side door. Before someone can enter through the side door, they must be directed to the main entrance so the standard check-in process happens.
constructor(s: String) : this(s.length)
The side door accepts a String, but this(s.length) redirects to the main entrance, which accepts an Int. After the main setup is complete, the secondary constructor can perform its own extra work.
Syntax and Examples
Use constructor inside the class body, followed by its parameters. Put : this(...) after the parameter list to delegate to another constructor in the same class.
class C(val a: Int) {
constructor(s: String) : this(s.length) {
println("Created C from the text: $s")
}
}
fun main() {
val fromNumber = C(5)
val fromText = C("hello")
println(fromNumber.a) // 5
println(fromText.a) // 5
}
C(5) calls the primary constructor directly.
C("hello") calls the secondary constructor. It calculates "hello".length, delegates to the primary constructor as this(5), then runs the secondary constructor body.
A secondary constructor can also delegate to another secondary constructor, but the chain must eventually reach the primary constructor when one exists:
class User(val name: String, val age: Int) {
constructor(name: String) : this(name, 0)
constructor() : this("Anonymous")
}
Step by Step Execution
Consider this code:
class Message(val text: String, val priority: Int) {
constructor(text: String) : this(text, 1) {
println("Using the default priority")
}
}
fun main() {
val message = Message("Server started")
println(message.priority)
}
When Message("Server started") runs:
- Kotlin finds a constructor that accepts one
String. - The secondary constructor delegates with
this(text, 1). - The primary constructor initializes
textto"Server started"andpriorityto1. - The secondary constructor body runs and prints
Using the default priority. - The completed
Messageobject is assigned tomessage. println(message.priority)prints1.
The output is:
Using the default priority
1
Real World Use Cases
Secondary constructors are useful when a class should accept several input formats but should initialize itself through one main path.
- Default values from a shorter input: Create a
Userwith only a name and choose a default role. - Parsing input: Accept a date string, parse it, then delegate using numeric date values.
- Convenience creation: Accept a configuration object in one constructor and individual values in another.
- Java interoperability: Java callers cannot always use Kotlin default arguments as conveniently, so constructors can provide Java-friendly overloads.
- Legacy APIs: Provide an alternate initialization form while keeping a consistent internal representation.
Example: accepting an RGB hex value while storing separate color channels.
class Color(val red: Int, val green: Int, val blue: Int) {
constructor(hex: String) : this(
hex.substring(1, 3).toInt(16),
hex.substring(3, 5).toInt(16),
hex.substring(5, 7).toInt(16)
)
}
Real Codebase Usage
In modern Kotlin codebases, developers often prefer primary constructor parameters with default values over many secondary constructors.
class Connection(
val host: String,
val port: Int = 443,
val useSsl: Boolean = true
)
This is concise for Kotlin callers:
val local = Connection("localhost", port = 8080, useSsl = false)
val secure = Connection("api.example.com")
Use a secondary constructor when it adds a genuinely different input form or conversion:
class Product(val id: Int, val name: String) {
constructor(csvLine: String) : this(
csvLine.substringBefore(",").toInt(),
csvLine.substringAfter(",")
)
}
For more complex parsing, validation, I/O, or error handling, a named factory function is often clearer because its name can describe what it does:
class Product private constructor(val id: Int, val name: String) {
companion object {
fun fromCsv(csvLine: String): Product {
val parts = csvLine.split(",", limit = 2)
require(parts.size == 2) { "Expected id,name" }
return Product(parts[0].toInt(), parts[1])
}
}
}
Factory names such as fromCsv, fromJson, or fromEnvironment make conversion work explicit.
Common Mistakes
Omitting the constructor keyword
This does not compile because this(...) cannot declare a constructor by itself:
class C(a: Int) {
this(s: String) : this(s.length)
}
Write constructor:
class C(val a: Int) {
constructor(s: String) : this(s.length)
}
Forgetting to delegate to the primary constructor
If a primary constructor exists, a secondary constructor must delegate to it.
class C(val a: Int) {
// Does not compile
constructor(s: String) {
println(s)
}
}
Correct version:
class C(val a: Int) {
constructor(s: String) : this(s.length) {
println(s)
}
}
Doing work before this(...)
Constructor delegation is part of the constructor declaration, not a statement that can be placed later in the body.
class C(val a: Int) {
// Invalid
constructor(s: String) {
println(s)
this(s.length)
}
}
Put delegation after the parameter list:
constructor(s: String) : this(s.length) {
println(s)
}
Expecting a to be a property
A primary constructor parameter is not automatically accessible as a property after construction. Add val or var when the object needs to store it.
class C(a: Int) // `a` is only available during initialization
class StoredC(val a: Int) // `a` is a readable property
Creating confusing overloads
Avoid constructors whose arguments are easy to mix up, especially multiple parameters of the same type. Prefer named factory functions or a configuration data class when clarity is more important than a short call.
Comparisons
| Approach | Best use | Example |
|---|---|---|
| Primary constructor | The normal, required initialization data | class User(val name: String) |
| Secondary constructor | A convenient alternate input form | constructor(email: String) : this(email.substringBefore("@")) |
| Default parameter | Optional values with a sensible default | class User(val name: String, val active: Boolean = true) |
| Named factory function | Parsing, validation, or creation that needs a descriptive name | fun fromCsv(line: String): User |
init block | Validation or setup shared by all construction paths | init { require(name.isNotBlank()) } |
A secondary constructor is not a replacement for an init block. init blocks run whenever the primary constructor is reached, including when construction starts from a secondary constructor.
class Account(val id: String) {
init {
require(id.isNotBlank())
}
constructor(idNumber: Int) : this(idNumber.toString())
}
Cheat Sheet
- Declare a secondary constructor inside the class body with
constructor. - Syntax:
constructor(parameters) : this(arguments) {
// optional extra work
}
this(...)delegates to another constructor in the same class.- If the class has a primary constructor, each secondary constructor must eventually call it.
- The delegated constructor completes its initialization before the secondary constructor body runs.
- Add
valorvarto primary constructor parameters that should become properties. - Prefer default parameters for simple optional values.
- Prefer a named factory function for parsing or complex creation logic.
class C(val value: Int) {
constructor(text: String) : this(text.length)
}
FAQ
What is a secondary constructor in Kotlin?
It is an additional constructor declared inside a class body using the constructor keyword. It provides another way to create instances of the class.
How do I call the primary constructor from a Kotlin secondary constructor?
Use : this(arguments) after the secondary constructor parameter list.
constructor(text: String) : this(text.length)
Why does this(s: String) not compile in Kotlin?
this is used for constructor delegation, not constructor declaration. A secondary constructor must start with constructor(s: String).
Must every Kotlin secondary constructor call the primary constructor?
Yes, when a primary constructor exists. It can call it directly or delegate through another secondary constructor that eventually calls it.
Can a Kotlin class have multiple secondary constructors?
Yes. Each constructor must have a distinct parameter signature.
Should I use secondary constructors or default arguments?
Use default arguments for simple optional values. Use a secondary constructor when callers provide meaningfully different input, such as a string that must be converted into primary constructor values.
Can secondary constructors validate data?
Yes, but shared validation usually belongs in an init block or primary-constructor validation, so it applies to every construction path.
Where is the Kotlin documentation for secondary constructors?
See the official Kotlin documentation section named Classes and Constructors, under Secondary constructors.
Mini Project
Description
Build a Temperature class that stores a temperature in Celsius while allowing callers to create it from either Celsius or Fahrenheit. This demonstrates an alternate input format delegated to one primary initialization path.
Goal
Create Temperature objects from Celsius values and Fahrenheit values, then display the normalized Celsius value.
Requirements
- Create a class with a primary constructor that stores a Celsius
Doublevalue. - Add a secondary constructor that accepts a Fahrenheit
Doublevalue. - Delegate from the Fahrenheit constructor to the primary constructor.
- Convert Fahrenheit to Celsius using
(fahrenheit - 32) * 5 / 9. - Print the Celsius values for one object created each way.
Keep learning
Related questions
Accessing Kotlin Extension Functions from Java
Learn how Kotlin extension functions are compiled and how to call them correctly from Java with clear examples and common pitfalls.
Add a Maven Repository URL in Gradle Kotlin DSL (build.gradle.kts)
Learn how to add Maven Central and a custom Maven repository URL in Gradle Kotlin DSL using build.gradle.kts syntax.
Allow HTTP and HTTPS in Android 9 Pie with Network Security Configuration
Learn how Android 9 Pie handles cleartext HTTP traffic and how to allow HTTP and HTTPS safely using network security config.