From 3bb91ccc1a5474da252fdd15e357931d0d76b02c Mon Sep 17 00:00:00 2001 From: KonstantinKhan Date: Fri, 15 Aug 2025 15:36:32 +0500 Subject: [PATCH 1/6] create examples Kotlin Data classes features --- .../kotlinfeatures/DataClass.kt | 129 +++++++++++++++ .../codecollection/kotlinfeatures/README.md | 23 +++ .../kotlinfeatures/DataClassTest.kt | 147 ++++++++++++++++++ 3 files changed, 299 insertions(+) create mode 100644 src/main/kotlin/codecollection/kotlinfeatures/DataClass.kt create mode 100644 src/main/kotlin/codecollection/kotlinfeatures/README.md create mode 100644 src/test/kotlin/codecollection/kotlinfeatures/DataClassTest.kt diff --git a/src/main/kotlin/codecollection/kotlinfeatures/DataClass.kt b/src/main/kotlin/codecollection/kotlinfeatures/DataClass.kt new file mode 100644 index 0000000..db8b6c1 --- /dev/null +++ b/src/main/kotlin/codecollection/kotlinfeatures/DataClass.kt @@ -0,0 +1,129 @@ +package codecollection.kotlinfeatures + +// Data classes can't be abstract, open, sealed, or inner. +// The primary constructor must have at least one parameter. + +data class Person( + val firstName: String, + val age: Int, + val email: String, +) + +// Attention! The use "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( + var 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 = "alice@example.com") + val person2 = Person(firstName = "Bob", age = 32, email = "bob@example.com") + val person3 = Person(firstName = "Alice", age = 30, email = "alice@example.com") + + // toString() - automatically generated + println("\n=== toString() ===") + println("Person 1: $person1") + println("Person 2: $person2") + // Output: + // Person 1: Person(firstName=Alice, age=30, email=alice@example.com) + // Person 2: Person(firstName=Bob, age=32, email=bob@example.com) + + // equals() + println("\n=== equals() ===") + println("person1 == person3: ${person1 == person3}") // true + println("person1 == person2: ${person1 == person2}") // false + + // hashCode() + println("\n=== hashCode() ===") + println("hashCode person1: ${person1.hashCode()}") + println("hashCode person2: ${person2.hashCode()}") + println("hashCode person3: ${person3.hashCode()}") // same as person1.hashCode() + // Output: + // hashCode person1: -1399032001 + // hashCode person2: 531733799 + // hashCode person3: -1399032001 + + // 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 is same key + + // Important! The use "var" is not recommended in the data classes. + println("\n=== Mutation properties: ===") + val mutablePerson = MutablePerson("Charlie", 35, "charlie@example.com") + val testMap = hashMapOf(mutablePerson to "Developer") + println("before mutation: ${testMap[mutablePerson]}") // Developer + mutablePerson.age = 36 + println("after mutation: ${testMap[mutablePerson]}") // null, the key is "lost" + + // copy() Attention! It is shallow copy. See example + println("\n=== copy() ===") + + val person1Updated = person1.copy(age = 31, email = "new.alice@example.com") + + println("Updated person 1: $person1Updated") + // Output: Updated person 1: Person(firstName=Alice, age=31, email=new.alice@example.com) + + 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=alice@example.com), address=Address(city=New York, street=Washington Street)) + // employee2: Employee(position=Developer, person=Person(firstName=Alice, age=30, email=alice@example.com), 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=alice@example.com), address=Address(city=New York, street=Park Avenue)) + // employee2: Employee(position=Developer, person=Person(firstName=Alice, age=30, email=alice@example.com), address=Address(city=New York, street=Park Avenue)) + + // 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=alice@example.com + + // 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: alice@example.com +} diff --git a/src/main/kotlin/codecollection/kotlinfeatures/README.md b/src/main/kotlin/codecollection/kotlinfeatures/README.md new file mode 100644 index 0000000..eba06a8 --- /dev/null +++ b/src/main/kotlin/codecollection/kotlinfeatures/README.md @@ -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)! \ No newline at end of file diff --git a/src/test/kotlin/codecollection/kotlinfeatures/DataClassTest.kt b/src/test/kotlin/codecollection/kotlinfeatures/DataClassTest.kt new file mode 100644 index 0000000..bf401fa --- /dev/null +++ b/src/test/kotlin/codecollection/kotlinfeatures/DataClassTest.kt @@ -0,0 +1,147 @@ +package codecollection.kotlinfeatures + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +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 = "alice@example.com") + val person2 = Person(firstName = "Bob", age = 32, email = "bob@example.com") + val person3 = Person(firstName = "Alice", age = 30, email = "alice@example.com") + + val mutablePerson = MutablePerson("Charlie", 35, "charlie@example.com") + + 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("alice@example.com", person1.email) + } + + @Test + fun `toString() should return formatted string`() { + assertEquals( + "Person(firstName=Alice, age=30, email=alice@example.com)", + person1.toString(), + ) + } + + @Test + fun `equals should work correctly for same content`() { + assertTrue(person1 == person3) + assertFalse(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 = "new.alice@example.com") + assertEquals(person1.firstName, updated.firstName) + assertEquals(person1.age, updated.age) + assertEquals("new.alice@example.com", updated.email) + assertEquals("alice@example.com", 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("alice@example.com", email) + } + + @Test + fun `componentN() should work correctly`() { + assertEquals("Alice", person1.component1()) + assertEquals(30, person1.component2()) + assertEquals("alice@example.com", person1.component3()) + } +} From 2170a23f87713e3df80fab8ea97f971aca9d1390 Mon Sep 17 00:00:00 2001 From: KonstantinKhan Date: Sun, 17 Aug 2025 11:43:00 +0500 Subject: [PATCH 2/6] change position properties to 'val' --- src/main/kotlin/codecollection/kotlinfeatures/DataClass.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/codecollection/kotlinfeatures/DataClass.kt b/src/main/kotlin/codecollection/kotlinfeatures/DataClass.kt index db8b6c1..4844a2f 100644 --- a/src/main/kotlin/codecollection/kotlinfeatures/DataClass.kt +++ b/src/main/kotlin/codecollection/kotlinfeatures/DataClass.kt @@ -1,8 +1,8 @@ -package codecollection.kotlinfeatures - // 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, @@ -22,7 +22,7 @@ data class Address( ) data class Employee( - var position: String = "Intern", // default property + val position: String = "Intern", // default property val person: Person, val address: Address, ) From 14eec50299ea5337fe90e2ba579864bcb25f2183 Mon Sep 17 00:00:00 2001 From: KonstantinKhan Date: Sun, 17 Aug 2025 12:18:27 +0500 Subject: [PATCH 3/6] update according to the review comments --- .../kotlinfeatures/DataClass.kt | 41 ++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/src/main/kotlin/codecollection/kotlinfeatures/DataClass.kt b/src/main/kotlin/codecollection/kotlinfeatures/DataClass.kt index 4844a2f..871947c 100644 --- a/src/main/kotlin/codecollection/kotlinfeatures/DataClass.kt +++ b/src/main/kotlin/codecollection/kotlinfeatures/DataClass.kt @@ -35,6 +35,14 @@ fun main() { val person2 = Person(firstName = "Bob", age = 32, email = "bob@example.com") val person3 = Person(firstName = "Alice", age = 30, email = "alice@example.com") + 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") @@ -42,21 +50,19 @@ fun main() { // Output: // Person 1: Person(firstName=Alice, age=30, email=alice@example.com) // Person 2: Person(firstName=Bob, age=32, email=bob@example.com) +} - // equals() +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 +} - // hashCode() +fun demonstrateToHashCode(person1: Person, person2: Person, person3: Person) { + // hashCode() - automatically generated println("\n=== hashCode() ===") - println("hashCode person1: ${person1.hashCode()}") - println("hashCode person2: ${person2.hashCode()}") - println("hashCode person3: ${person3.hashCode()}") // same as person1.hashCode() - // Output: - // hashCode person1: -1399032001 - // hashCode person2: 531733799 - // hashCode person3: -1399032001 + println("person1.hashCode() equals person3.hashCode(): ${person1.hashCode() == person3.hashCode()}") // true // Checking in HashSet println("\n=== Checking in HashSet ===") @@ -77,17 +83,21 @@ fun main() { ) println("person1 salary: ${salary[person1]}") // 30000 - println("person3 salary: ${salary[person3]}") // 30000, because is same key + 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, "charlie@example.com") - val testMap = hashMapOf(mutablePerson to "Developer") - println("before mutation: ${testMap[mutablePerson]}") // Developer + val roleMap = hashMapOf(mutablePerson to "Developer") + println("before mutation: ${mutablePerson.hashCode()}") mutablePerson.age = 36 - println("after mutation: ${testMap[mutablePerson]}") // null, the key is "lost" + println("after mutation: ${mutablePerson.hashCode()}") // Different! + println("Map size: ${roleMap.size}") // Map still contains the entry but can't find it. +} - // copy() Attention! It is shallow copy. See example +fun demonstrateToCopy(person1: Person) { + // copy() - automatically generated. + // Attention! It is shallow copy. See example println("\n=== copy() ===") val person1Updated = person1.copy(age = 31, email = "new.alice@example.com") @@ -111,7 +121,9 @@ fun main() { println("employee2: $employee2") // employee1: Employee(position=Developer, person=Person(firstName=Alice, age=30, email=alice@example.com), address=Address(city=New York, street=Park Avenue)) // employee2: Employee(position=Developer, person=Person(firstName=Alice, age=30, email=alice@example.com), 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 @@ -127,3 +139,4 @@ fun main() { // age: 30 // email: alice@example.com } + From 287ebdf10c2a87da98ae0709dbc458015b544385 Mon Sep 17 00:00:00 2001 From: KonstantinKhan Date: Sun, 17 Aug 2025 12:19:14 +0500 Subject: [PATCH 4/6] replace assert methods --- .../kotlin/codecollection/kotlinfeatures/DataClassTest.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/test/kotlin/codecollection/kotlinfeatures/DataClassTest.kt b/src/test/kotlin/codecollection/kotlinfeatures/DataClassTest.kt index bf401fa..b81f3c5 100644 --- a/src/test/kotlin/codecollection/kotlinfeatures/DataClassTest.kt +++ b/src/test/kotlin/codecollection/kotlinfeatures/DataClassTest.kt @@ -2,7 +2,6 @@ package codecollection.kotlinfeatures import kotlin.test.Test import kotlin.test.assertEquals -import kotlin.test.assertFalse import kotlin.test.assertNotEquals import kotlin.test.assertNotSame import kotlin.test.assertNull @@ -38,8 +37,8 @@ class DataClassTest { @Test fun `equals should work correctly for same content`() { - assertTrue(person1 == person3) - assertFalse(person1 == person2) + assertEquals(person1, person3) + assertNotEquals(person1, person2) } @Test From 45ed0ee5fd3e7663108ecf641a8bd18e834dc063 Mon Sep 17 00:00:00 2001 From: KonstantinKhan Date: Sun, 17 Aug 2025 12:23:15 +0500 Subject: [PATCH 5/6] format --- src/main/kotlin/codecollection/kotlinfeatures/DataClass.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/kotlin/codecollection/kotlinfeatures/DataClass.kt b/src/main/kotlin/codecollection/kotlinfeatures/DataClass.kt index 871947c..5e0cc7d 100644 --- a/src/main/kotlin/codecollection/kotlinfeatures/DataClass.kt +++ b/src/main/kotlin/codecollection/kotlinfeatures/DataClass.kt @@ -139,4 +139,3 @@ fun demonstrateToDestructuring(person1: Person) { // age: 30 // email: alice@example.com } - From e017a63b25a8b6aeb71985eb7fa7c3a5c456e7cd Mon Sep 17 00:00:00 2001 From: e5LA <208197507+e5LA@users.noreply.github.com> Date: Sat, 23 Aug 2025 12:16:11 +0200 Subject: [PATCH 6/6] refactor: update the comment --- src/main/kotlin/codecollection/kotlinfeatures/DataClass.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/codecollection/kotlinfeatures/DataClass.kt b/src/main/kotlin/codecollection/kotlinfeatures/DataClass.kt index 5e0cc7d..2573072 100644 --- a/src/main/kotlin/codecollection/kotlinfeatures/DataClass.kt +++ b/src/main/kotlin/codecollection/kotlinfeatures/DataClass.kt @@ -9,7 +9,7 @@ data class Person( val email: String, ) -// Attention! The use "var" is not recommended in the data classes. See the example. +// Attention! Using 'var' is not recommended in the data classes. See the example. data class MutablePerson( var firstName: String, var age: Int,