Kotlin and Copilot: create a person class with a name
Video: Kotlin and Copilot: create a person class with a name by Taught by Celeste AI - AI Coding Coach
Kotlin: A Person Class With Just a Name
class Person(val name: String) { fun printName() = println("Name: $name") }. The simplest meaningful class. Property in the constructor; one method that uses it.
A minimalist version of episode 34. One property, one method. The "Hello, World!" of Kotlin classes.
The Copilot prompt
// A simple Person class with just a name property
class Person(val name: String) {
Copilot generates:
class Person(val name: String) {
fun printName() {
println("Name: $name")
}
}
fun main() {
val person = Person("Alice")
person.printName() // "Name: Alice"
}
Walkthrough
class Person(val name: String) — primary constructor takes name, makes it a public read-only property.
fun printName() reads name (no this. needed) and prints. Same shape as episode 34's greet(), just renamed.
When you only need data, use data class
If Person doesn't have any behavior beyond holding a name:
data class Person(val name: String)
val a = Person("Alice")
val b = Person("Alice")
println(a == b) // true (structural equality)
println(a) // "Person(name=Alice)"
val (name) = a // destructuring; name = "Alice"
data class auto-generates:
- equals / hashCode (structural)
- toString (shows all properties)
- copy(...) for instance with one field changed
- componentN() for destructuring
For one-property records, this is overkill — but harmless and future-proof for when you add a second field.
Plain class vs data class
| Feature | class Person(val name: String) |
data class Person(val name: String) |
|---|---|---|
| Equality | Reference (a == b only when same instance) |
Structural (a == b if same name) |
| Hashing | Identity hash | Hash of properties |
| toString | Class name + memory address | Shows properties |
| Destructure | No (without componentN) |
Yes |
| Copy | No (without copy method) |
Yes |
For value-like types (records, immutable data), data class. For identity-bearing entities (User in a database with an ID), plain class.
Adding methods
class Person(val name: String) {
fun printName() = println("Name: $name")
fun describe() = "Person named $name"
fun nameUpper() = name.uppercase()
}
Three methods. Each reads name. Adding more methods is the same pattern.
For a lot of methods, consider whether they all belong to the class or if some are better as extension functions or free utilities.
Properties with custom getters
For derived properties:
class Person(val name: String) {
val firstLetter: Char
get() = name.first()
val displayName: String
get() = name.replaceFirstChar { it.uppercaseChar() }
}
val p = Person("alice")
println(p.firstLetter) // 'a'
println(p.displayName) // "Alice"
val firstLetter: Char declares the property; get() = ... is the custom getter. No backing field — recomputed on each access.
For caching:
class Person(val name: String) {
val displayName: String by lazy {
name.replaceFirstChar { it.uppercaseChar() }
}
}
by lazy { ... } computes once, caches. Useful for expensive derivations.
Equality and hashing
For data class, Kotlin generates structural equality:
data class Person(val name: String)
val a = Person("Alice")
val b = Person("Alice")
println(a == b) // true
println(a.hashCode() == b.hashCode()) // true
println(a === b) // false (different instances)
For plain class, you'd write them yourself:
class Person(val name: String) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Person) return false
return name == other.name
}
override fun hashCode(): Int = name.hashCode()
override fun toString(): String = "Person(name=$name)"
}
That's a lot of code for what data class does in one line.
In a list
val people = listOf(
Person("Alice"),
Person("Bob"),
Person("Charlie"),
)
people.forEach { it.printName() }
// Name: Alice
// Name: Bob
// Name: Charlie
val names = people.map { it.name } // ["Alice", "Bob", "Charlie"]
The Person class is just a value type — fits naturally into list operations.
Common mistakes
Forgetting val. class Person(name: String) — name is a constructor parameter, not a property. Person("Alice").name errors.
Using var when val is enough. var makes the property mutable. For immutable records, always val.
Plain class for a record. Comparing two Persons with == returns false (different instances) — usually surprising. Use data class.
Skipping printName() thinking the constructor printed it. Constructor only sets the property. init { println(...) } would print on construction; printName() prints when called.
Confusing === and ==. === is reference equality (same instance). == is equals (structural for data class, reference for plain class).
What's next
Episode 36: List of months in 3-letter format. Generate ["JAN", "FEB", ...] from java.time.Month.
Recap
class Person(val name: String) { fun printName() = println("Name: $name") }. Primary-constructor val name makes it a public read-only property. data class Person(val name: String) adds equals/hashCode/copy/toString automatically. For derived properties, val x get() = ...; for cached, val x by lazy { ... }. Plain class for identity types; data class for value types.
Next episode: months in 3-letter format.