Skip to content

Commit

Permalink
✨ Add structural equality operations to EmailAddress (#635)
Browse files Browse the repository at this point in the history
Adds the `equals(Any?)` and the `hashCode()` functions to the `EmailAddress` type in the `org.kotools.types` package.
  • Loading branch information
LVMVRQUXL committed Apr 1, 2024
1 parent f0eccb4 commit d2e4f44
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 1 deletion.
8 changes: 8 additions & 0 deletions src/commonMain/kotlin/org/kotools/types/AnyX.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.kotools.types

internal fun hashCodeOf(first: Any, vararg others: Any): Int {
val prime = 31
var result: Int = prime + first.hashCode()
others.forEach { result = prime * result + it.hashCode() }
return result
}
65 changes: 65 additions & 0 deletions src/commonMain/kotlin/org/kotools/types/EmailAddress.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,69 @@ import kotlin.jvm.JvmStatic
@ExperimentalKotoolsTypesApi
@ExperimentalSince(KotoolsTypesVersion.Unreleased)
public class EmailAddress private constructor(private val value: String) {
// -------------------- Structural equality operations ---------------------

/**
* Returns `true` if the [other] object is an instance of [EmailAddress] and
* has the same string representation as this email address, or returns
* `false` otherwise.
*
* <br>
* <details open>
* <summary>
* <b>Calling from Kotlin</b>
* </summary>
*
* Here's an example of calling this function from Kotlin code:
*
* SAMPLE: EmailAddressKotlinSample.equals_override.md
* </details>
*
* <br>
* <details>
* <summary>
* <b>Calling from Java</b>
* </summary>
*
* Here's an example of calling this function from Java code:
*
* SAMPLE: EmailAddressJavaSample.equals_override.md
* </details>
*/
@Suppress("RedundantModalityModifier")
final override fun equals(other: Any?): Boolean =
other is EmailAddress && this.value == other.value

/**
* Returns a hash code value for this email address.
*
* <br>
* <details open>
* <summary>
* <b>Calling from Kotlin</b>
* </summary>
*
* Here's an example of calling this function from Kotlin code:
*
* SAMPLE: EmailAddressKotlinSample.hashCode_override.md
* </details>
*
* <br>
* <details>
* <summary>
* <b>Calling from Java</b>
* </summary>
*
* Here's an example of calling this function from Java code:
*
* SAMPLE: EmailAddressJavaSample.hashCode_override.md
* </details>
*/
@Suppress("RedundantModalityModifier")
final override fun hashCode(): Int = hashCodeOf(value)

// ------------------------------ Converters -------------------------------

/**
* Returns the string representation of this email address.
*
Expand Down Expand Up @@ -41,6 +104,8 @@ public class EmailAddress private constructor(private val value: String) {
@Suppress("RedundantModalityModifier")
final override fun toString(): String = value

// -------------------------------------------------------------------------

/** Contains static declarations for the [EmailAddress] type. */
public companion object {
/**
Expand Down
59 changes: 59 additions & 0 deletions src/commonTest/kotlin/org/kotools/types/EmailAddressTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,71 @@ import kotools.types.experimental.ExperimentalKotoolsTypesApi
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.assertNotEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue

@OptIn(ExperimentalKotoolsTypesApi::class)
class EmailAddressTest {
@Test
fun structural_equality_should_pass_with_the_same_instance() {
val first: EmailAddress = EmailAddress.fromString("[email protected]")
val second: Any = first
val equality: Boolean = first.equals(second)
assertTrue(equality)
val firstHashCode: Int = first.hashCode()
val secondHashCode: Int = second.hashCode()
assertEquals(firstHashCode, secondHashCode)
}

@Test
fun structural_equality_should_pass_with_another_EmailAddress_having_the_same_string_representation() {
val value: Any = "[email protected]"
val first: EmailAddress = EmailAddress.fromString(value)
val second: Any = EmailAddress.fromString(value)
val equality: Boolean = first.equals(second)
assertTrue(equality)
val firstHashCode: Int = first.hashCode()
val secondHashCode: Int = second.hashCode()
assertEquals(firstHashCode, secondHashCode)
}

@Test
fun structural_equality_should_fail_with_null() {
val first: EmailAddress = EmailAddress.fromString("[email protected]")
val second: Any? = null
val equality: Boolean = first.equals(second)
assertFalse(equality)
val firstHashCode: Int = first.hashCode()
val secondHashCode: Int = second.hashCode()
assertNotEquals(firstHashCode, secondHashCode)
}

@Test
fun structural_equality_should_fail_with_another_object_having_another_type_than_EmailAddress() {
val value: Any = "[email protected]"
val first: EmailAddress = EmailAddress.fromString(value)
val second: Any = value
val equality: Boolean = first.equals(second)
assertFalse(equality)
val firstHashCode: Int = first.hashCode()
val secondHashCode: Int = second.hashCode()
assertNotEquals(firstHashCode, secondHashCode)
}

@Test
fun structural_equality_should_fail_with_another_EmailAddress_having_another_string_representation() {
val first: EmailAddress = EmailAddress.fromString("[email protected]")
val second: Any = EmailAddress.fromString("[email protected]")
val equality: Boolean = first.equals(second)
assertFalse(equality)
val firstHashCode: Int = first.hashCode()
val secondHashCode: Int = second.hashCode()
assertNotEquals(firstHashCode, secondHashCode)
}

@Test
fun toString_should_pass() {
val value: Any = "[email protected]"
Expand Down
18 changes: 18 additions & 0 deletions subprojects/samples/src/main/java/EmailAddressJavaSample.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
package org.kotools.types;

class EmailAddressJavaSample {
void equals_override() {
final Object value = "[email protected]";
final EmailAddress first = EmailAddress.fromString(value);
final EmailAddress second = EmailAddress.fromString(value);
final boolean result = first.equals(second);
System.out.println(result); // true
} // END

void hashCode_override() {
final Object value = "[email protected]";
final int first = EmailAddress.fromString(value)
.hashCode(); // TABS: 2
final int second = EmailAddress.fromString(value)
.hashCode(); // TABS: 2
final boolean result = first == second;
System.out.println(result); // true
} // END

void toString_override() {
final Object value = "[email protected]";
final EmailAddress address = EmailAddress.fromString(value);
Expand Down
18 changes: 18 additions & 0 deletions subprojects/samples/src/main/kotlin/EmailAddressKotlinSample.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,24 @@ import kotools.types.experimental.ExperimentalKotoolsTypesApi

@OptIn(ExperimentalKotoolsTypesApi::class)
internal object EmailAddressKotlinSample {
@Suppress("FunctionName")
fun equals_override() {
val value: Any = "[email protected]"
val first: EmailAddress = EmailAddress.fromString(value)
val second: EmailAddress = EmailAddress.fromString(value)
val result: Boolean = first == second // or first.equals(second)
println(result) // true
} // END

@Suppress("FunctionName")
fun hashCode_override() {
val value: Any = "[email protected]"
val first: EmailAddress = EmailAddress.fromString(value)
val second: EmailAddress = EmailAddress.fromString(value)
val result: Boolean = first.hashCode() == second.hashCode()
println(result) // true
} // END

@Suppress("FunctionName")
fun toString_override() {
val value: Any = "[email protected]"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,20 @@
import org.junit.jupiter.api.Test;

class EmailAddressJavaSampleTest {
private final EmailAddressJavaSample sample = new EmailAddressJavaSample();

@Test
void equals_override_should_pass() {
Assert.printsTrue(sample::equals_override);
}

@Test
void hashCode_override_should_pass() {
Assert.printsTrue(sample::hashCode_override);
}

@Test
void toString_override_should_pass() {
final EmailAddressJavaSample sample = new EmailAddressJavaSample();
Assert.printsTrue(sample::toString_override);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@ package org.kotools.types
import kotlin.test.Test

class EmailAddressKotlinSampleTest {
@Test
fun `equals(nullable Any) should pass`(): Unit =
assertPrintsTrue(EmailAddressKotlinSample::equals_override)

@Test
fun `hashCode() should pass`(): Unit =
assertPrintsTrue(EmailAddressKotlinSample::hashCode_override)

@Test
fun `toString() should pass`(): Unit =
assertPrintsTrue(EmailAddressKotlinSample::toString_override)
Expand Down

0 comments on commit d2e4f44

Please sign in to comment.