Tuesday, 4 March 2025

Kotlin Operator Overloading

So my colleague mentioned operator overloading, and how much fun it is.

So I wrote a little test.

package org.mrbear.kotlin.operators
import org.assertj.core.api.Assertions.assertThat
import org.mrbear.kotlin.AddressFactory
import org.mrbear.kotlin.Person
import org.mrbear.kotlin.PersonFactory
import org.testng.annotations.Test
class OperatorOverloadingTest {
@Test
fun testStandardInOperator() {
val list = listOf("A", "B", "C")
assertThat("A" in list).isTrue()
assertThat("D" in list).isFalse()
val str = "MRBEAR"
assertThat("E" in str).isTrue()
assertThat("H" in str).isFalse()
}
@Test
fun testCustomInOperator() {
val addressBook =
AddressBook(listOf(PersonFactory.alexanderGrahamnBell, PersonFactory.mrBear, PersonFactory.lordKelvin))
assertThat("Kelvin" in addressBook).isTrue()
assertThat("Mr. Bear" in addressBook).isFalse()
assertThat(PersonFactory.alexanderGrahamnBell in addressBook).isTrue()
assertThat(PersonFactory.smith in addressBook).isFalse()
}
@Test
fun testCustomNotInOperator() {
val addressBook =
AddressBook(listOf(PersonFactory.alexanderGrahamnBell, PersonFactory.mrBear, PersonFactory.lordKelvin))
assertThat(PersonFactory.alexanderGrahamnBell notIn addressBook).isFalse()
assertThat(PersonFactory.smith notIn addressBook).isTrue()
}
@Test
fun testCustomNotOperator() {
assertThat(PersonFactory.alexanderGrahamnBell.not()).isEqualTo(Person("notAlexander", "Graham", "Bell", 55, AddressFactory.addressInAmerica))
assertThat(!PersonFactory.alexanderGrahamnBell).isEqualTo(Person("notAlexander", "Graham", "Bell", 55, AddressFactory.addressInAmerica))
}
}
operator fun Person.not(): Person =
Person("not${this.firstName}", this.middleName, this.lastName, this.age, this.address)
infix fun Person.notIn(addressBook: AddressBook): Boolean = !(this in addressBook)
class AddressBook(private val listOf: List<Person>) {
operator fun contains(lastName: String): Boolean {
return listOf.map { it.lastName.uppercase() }.contains(lastName.uppercase())
}
operator fun contains(person: Person): Boolean {
return listOf.contains(person)
}
}

It works pretty good, but for infix functions it has a very high "syntactic sugar" and less about "real" operator overloading.

Also, the reference page in [1] indicates that for Infix functions, the precedence has fixed rules that do not (always) conform to precedence that we would assume.

My colleague told me that a number of infix functions were created for QueryDSL, so we could write a semblance of SQL in Kotlin, but the precedence tends to screw things up.

We removed these infix functions from our source code again.

So, use sparingly and only when it makes sense. For example if it makes your code a magnitude easier to read/reason about and preferable with as small a scope as possible.

References

[1] KotlinLang - Functions
https://kotlinlang.org/docs/functions.html#function-scope
Baeldung - Operator Overloading in Kotlin
https://www.baeldung.com/kotlin/operator-overloading
Baeldung - Infix Functions in Kotlin
https://www.baeldung.com/kotlin/infix-functions

Thursday, 20 February 2025

Kotlin: listOf() versus emptyList()

So I noticed that Kotlin tends to have several ways of doing the same thing1. Sometimes these are actually identical, sometimes there are subtle differences.

That makes things hard for me.

Let's take this trivial example.

@Test
fun testEmpty() {
val emptyList: List<String> = listOf()
val anotherEmptyList: List<String> = emptyList()
assertThat(emptyList).isEmpty()
assertThat(anotherEmptyList).isEmpty()
assertThat(emptyList).isEqualTo(anotherEmptyList);
assertThat(emptyList).isSameAs(anotherEmptyList);
}
view raw EmpyTest.kt hosted with ❤ by GitHub

There is no difference. The one thing you could mention is that emptyList() more accurately conveys what you are trying to do.

There's something similar in Java. Collections.emptyList() versus List.of(). The comments on [2] seems to be very interesting.

In Java one could argue that List.of() is new, so should be used. And we could assume that the Language Architects in Java wouldn't add a List.of() for no reason.

References

[1] Kotlinlanguage Reference - emptyList
https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.collections/empty-list.html
[2] StackOverflow - List.of() or Collections.emptyList()
https://stackoverflow.com/questions/39400238/list-of-or-collections-emptylist

Thursday, 6 February 2025

Java - Behaviour of Equals in Collections

I just thought I'd write some things down that I already know, but sometimes it's nice to see this proven.

It's a very very beginner Java subject, but people won't fault me for blogging about it.

It might be of some use to somebody.

public class EqualsTest {
public enum Day {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY}
/**
* Sets in Java are un-ordered. Two sets are equal if they contain the same elements.
*/
@Test
public void testEqualsSet() {
Set<Day> weekDays = Set.of(Day.MONDAY, Day.TUESDAY, Day.WEDNESDAY, Day.THURSDAY, Day.FRIDAY);
Set<Day> otherWeekDays = Set.of(Day.MONDAY, Day.TUESDAY, Day.WEDNESDAY, Day.FRIDAY, Day.THURSDAY);
Set<Day> weekendDays = Set.of(Day.SUNDAY, Day.SATURDAY);
Set<Day> otherweekendDays = new HashSet<>(Set.of(Day.SATURDAY, Day.SUNDAY));
assertThat(weekDays.equals(weekendDays)).isFalse();
assertThat(weekDays.equals(otherWeekDays)).isTrue();
assertThat(weekendDays.equals(otherweekendDays)).isTrue();
}
/**
* Lists in Java are Ordered. A different ordering, means two lists are not equal.
*/
@Test
public void testEqualsList() {
List<Day> weekDays = List.of(Day.MONDAY, Day.TUESDAY, Day.WEDNESDAY, Day.THURSDAY, Day.FRIDAY);
List<Day> otherWeekDays = List.of(Day.TUESDAY, Day.WEDNESDAY, Day.THURSDAY, Day.FRIDAY, Day.MONDAY);
List<Day> weekendDays = List.of(Day.SATURDAY, Day.SUNDAY);
List<Day> otherweekendDays = List.of(Day.SUNDAY, Day.SATURDAY);
assertThat(weekDays.equals(weekendDays)).isFalse();
assertThat(weekDays.equals(otherWeekDays)).isFalse();
assertThat(weekendDays.equals(otherweekendDays)).isFalse();
}
/**
* So, what happens if we use TreeSets, that are Ordered?
*/
@Test
public void testEqualsTreeSet() {
Set<Day> weekDays = new TreeSet<>(Set.of(Day.MONDAY, Day.TUESDAY, Day.WEDNESDAY, Day.THURSDAY, Day.FRIDAY));
Set<Day> otherWeekDays = new TreeSet<>(Set.of(Day.MONDAY, Day.TUESDAY, Day.WEDNESDAY, Day.FRIDAY, Day.THURSDAY));
Set<Day> weekendDays = new TreeSet<>(Set.of(Day.SUNDAY, Day.SATURDAY));
Set<Day> otherweekendDays = new TreeSet<>(new HashSet<>(Set.of(Day.SATURDAY, Day.SUNDAY)));
assertThat(weekDays.equals(weekendDays)).isFalse();
assertThat(weekDays.equals(otherWeekDays)).isTrue();
assertThat(weekendDays.equals(otherweekendDays)).isTrue();
}
}
view raw EqualsTest.java hosted with ❤ by GitHub