Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 141 additions & 0 deletions src/main/kotlin/codecollection/kotlinfeatures/DataClass.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Data classes can't be abstract, open, sealed, or inner.
// The primary constructor must have at least one parameter.

package codecollection.kotlinfeatures

data class Person(
val firstName: String,
val age: Int,
val email: String,
)

// Attention! Using 'var' is not recommended in the data classes. See the example.
data class MutablePerson(
var firstName: String,
var age: Int,
var email: String,
)

data class Address(
var city: String,
var street: String,
)

data class Employee(
val position: String = "Intern", // default property
val person: Person,
val address: Address,
)

fun main() {
println("=== BASIC DATA CLASS FEATURES ===")

// Create instances
val person1 = Person(firstName = "Alice", age = 30, email = "[email protected]")
val person2 = Person(firstName = "Bob", age = 32, email = "[email protected]")
val person3 = Person(firstName = "Alice", age = 30, email = "[email protected]")

demonstrateToString(person1, person2)
demonstrateToEquals(person1, person2, person3)
demonstrateToHashCode(person1, person2, person3)
demonstrateToCopy(person1)
demonstrateToDestructuring(person1)
}

fun demonstrateToString(person1: Person, person2: Person) {
// toString() - automatically generated
println("\n=== toString() ===")
println("Person 1: $person1")
println("Person 2: $person2")
// Output:
// Person 1: Person(firstName=Alice, age=30, [email protected])
// Person 2: Person(firstName=Bob, age=32, [email protected])
}

fun demonstrateToEquals(person1: Person, person2: Person, person3: Person) {
// equals() - automatically generated
println("\n=== equals() ===")
println("person1 == person3: ${person1 == person3}") // true
println("person1 == person2: ${person1 == person2}") // false
}

fun demonstrateToHashCode(person1: Person, person2: Person, person3: Person) {
// hashCode() - automatically generated
println("\n=== hashCode() ===")
println("person1.hashCode() equals person3.hashCode(): ${person1.hashCode() == person3.hashCode()}") // true

// Checking in HashSet
println("\n=== Checking in HashSet ===")
val peopleSet = hashSetOf(person1, person2)
println("set contains person1: ${peopleSet.contains(person1)}") // true
println("set contains person3: ${peopleSet.contains(person3)}") // true, because hashCode() and equals() are the same
// Output:
// set contains person1: true
// set contains person3: true

// Checking in HashMap
println("\n=== Checking in HashMap ===")

val salary =
hashMapOf(
person1 to 30000,
person2 to 27000,
)

println("person1 salary: ${salary[person1]}") // 30000
println("person3 salary: ${salary[person3]}") // 30000, because person3 equals person1 (same hashCode and equals)

// Important! The use "var" is not recommended in the data classes.
println("\n=== Mutation properties: ===")
val mutablePerson = MutablePerson("Charlie", 35, "[email protected]")
val roleMap = hashMapOf(mutablePerson to "Developer")
println("before mutation: ${mutablePerson.hashCode()}")
mutablePerson.age = 36
println("after mutation: ${mutablePerson.hashCode()}") // Different!
println("Map size: ${roleMap.size}") // Map still contains the entry but can't find it.
}

fun demonstrateToCopy(person1: Person) {
// copy() - automatically generated.
// Attention! It is shallow copy. See example
println("\n=== copy() ===")

val person1Updated = person1.copy(age = 31, email = "[email protected]")

println("Updated person 1: $person1Updated")
// Output: Updated person 1: Person(firstName=Alice, age=31, [email protected])

println("\n=== Shallow copy: ===")
val originalAddress = Address("New York", "Washington Street")
val employee1 = Employee("Developer", person1, originalAddress)
val employee2 = employee1.copy()
println("before mutation:")
println("employee1: $employee1")
println("employee2: $employee2")
// employee1: Employee(position=Developer, person=Person(firstName=Alice, age=30, [email protected]), address=Address(city=New York, street=Washington Street))
// employee2: Employee(position=Developer, person=Person(firstName=Alice, age=30, [email protected]), address=Address(city=New York, street=Washington Street))

employee2.address.street = "Park Avenue"
println("after mutation:")
println("employee1: $employee1") // The street has changed too!
println("employee2: $employee2")
// employee1: Employee(position=Developer, person=Person(firstName=Alice, age=30, [email protected]), address=Address(city=New York, street=Park Avenue))
// employee2: Employee(position=Developer, person=Person(firstName=Alice, age=30, [email protected]), address=Address(city=New York, street=Park Avenue))
}

fun demonstrateToDestructuring(person1: Person) {
// Destructuring - automatically generated componentN() functions corresponding to the properties in their order of declaration.
println("\n=== Destructuring: ===")
val (name, age, email) = person1
println("Destructured: name=$name, age=$age, email=$email")
// Output: Destructured: name=Alice, age=30, [email protected]

// You can also use componentN() functions directly
println("name: ${person1.component1()}")
println("age: ${person1.component2()}")
println("email: ${person1.component3()}")
// Output:
// name: Alice
// age: 30
// email: [email protected]
}
23 changes: 23 additions & 0 deletions src/main/kotlin/codecollection/kotlinfeatures/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# 🛠️ Kotlin Features

This package contains examples of **Kotlin features**.

Each feature is implemented in its own file and is tested with a corresponding unit test - keeping the code clean, minimal, and easy to understand.

Whether you're learning, referencing, or contributing - this collection is for you.

---

## 🗂️ Available Features

| Feature Name | Description | File |
|-----------------------|----------------------------|--------------------------------|
| Data classes | Kotlin data class features | [`DataClass.kt`](DataClass.kt) |
| _...more coming soon_ |

---

## 🙌 Want to Help?

- Check out the [issues labeled `type: kotlinfeature`](https://github.com/e5LA/kotlin-code-collection/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22type%3A%20kotlinfeature%22)
- Or submit your own idea as new [Issue](https://github.com/e5LA/kotlin-code-collection/issues/new)!
146 changes: 146 additions & 0 deletions src/test/kotlin/codecollection/kotlinfeatures/DataClassTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package codecollection.kotlinfeatures

import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
import kotlin.test.assertNotSame
import kotlin.test.assertNull
import kotlin.test.assertSame
import kotlin.test.assertTrue

class DataClassTest {
val person1 = Person(firstName = "Alice", age = 30, email = "[email protected]")
val person2 = Person(firstName = "Bob", age = 32, email = "[email protected]")
val person3 = Person(firstName = "Alice", age = 30, email = "[email protected]")

val mutablePerson = MutablePerson("Charlie", 35, "[email protected]")

val address = Address("New York", "Washington Street")

val employee1 = Employee("Developer", person1, address)
val employeeDefault = Employee(person = person1, address = address)

@Test
fun `should create person with correct properties`() {
assertEquals("Alice", person1.firstName)
assertEquals(30, person1.age)
assertEquals("[email protected]", person1.email)
}

@Test
fun `toString() should return formatted string`() {
assertEquals(
"Person(firstName=Alice, age=30, [email protected])",
person1.toString(),
)
}

@Test
fun `equals should work correctly for same content`() {
assertEquals(person1, person3)
assertNotEquals(person1, person2)
}

@Test
fun `hashCode should be same for equal objects`() {
assertEquals(person1.hashCode(), person3.hashCode())
assertNotEquals(person1.hashCode(), person2.hashCode())
}

@Test
fun `should work correctly in hashset`() {
val peopleSet = hashSetOf(person1, person2)
assertTrue(peopleSet.contains(person1))
assertTrue(peopleSet.contains(person2))
assertTrue(peopleSet.contains(person3))
assertEquals(2, peopleSet.size)
}

@Test
fun `should work correctly in hashmap`() {
val salary =
hashMapOf(
person1 to 30000,
person2 to 27000,
)
assertEquals(30000, salary[person1])
assertEquals(27000, salary[person2])
assertEquals(30000, salary[person3])
assertEquals(2, salary.size)
}

@Test
fun `mutation var properties should break hashmap key`() {
val testMap = hashMapOf(mutablePerson to "Developer")
assertEquals("Developer", testMap[mutablePerson])
mutablePerson.age = 36
assertNull(testMap[mutablePerson])
}

@Test
fun `mutation var properties should change hashCode()`() {
val originalHashCode = mutablePerson.hashCode()
mutablePerson.age = 36
val newHashCode = mutablePerson.hashCode()
assertNotEquals(originalHashCode, newHashCode)
}

@Test
fun `copy() should create new instance with identical properties`() {
val copy = person1.copy()
assertEquals(person1, copy)
assertNotSame(person1, copy)
}

@Test
fun `copy() should create new instance with updated properties`() {
val updated = person1.copy(email = "[email protected]")
assertEquals(person1.firstName, updated.firstName)
assertEquals(person1.age, updated.age)
assertEquals("[email protected]", updated.email)
assertEquals("[email protected]", person1.email)
}

@Test
fun `copy() should perform shallow copy of reference types`() {
val employee2 = employee1.copy()

assertNotSame(employee1, employee2)
assertSame(employee1.person, employee2.person)
assertSame(employee1.address, employee2.address)
}

@Test
fun `mutation of referenced objects should affect the original and its copy`() {
val employee2 = employee1.copy()
employee2.address.street = "Park Avenue"
assertEquals("Park Avenue", employee1.address.street)
assertEquals("Park Avenue", employee2.address.street)
}

@Test
fun `should use default value when not specified`() {
assertEquals("Intern", employeeDefault.position)
}

@Test
fun `copy() use default value`() {
val copiedEmployee = employeeDefault.copy()
assertEquals("Intern", copiedEmployee.position)
}

@Test
fun `destructuring should work correctly`() {
val (name, age, email) = person1
assertEquals("Alice", name)
assertEquals(30, age)
assertEquals("[email protected]", email)
}

@Test
fun `componentN() should work correctly`() {
assertEquals("Alice", person1.component1())
assertEquals(30, person1.component2())
assertEquals("[email protected]", person1.component3())
}
}