Skip to main content

[Kotlin] Infix Functions

· One min read

In Kotlin, there is a method of defining functions called Infix functions, which is a syntax that was unimaginable while using Java as the primary language. Let's introduce this for those who are starting with Kotlin.

Member functions with a single parameter can be converted into Infix functions.

One of the prominent examples of Infix functions is the to function included in the standard library.

val pair = "Ferrari" to "Katrina"
println(pair)
// (Ferrari, Katrina)

You can define new Infix functions like to as needed. For example, you can extend Int as follows:

infix fun Int.times(str: String) = str.repeat(this)
println(2 times "Hello ")
// Hello Hello

If you want to redefine to as a new Infix function called onto, you can write it as follows:

infix fun String.onto(other: String) = Pair(this, other)
val myPair = "McLaren" onto "Lucas"
println(myPair)
// (McLaren, Lucas)

Such Kotlin syntax enables quite unconventional coding methods.

class Person(val name: String) {
val likedPeople = mutableListOf<Person>()

infix fun likes(other: Person) {
likedPeople.add(other)
}
}

fun main() {
val sophia = Person("Sophia")
val claudia = Person("Claudia")

sophia likes claudia // !!
}

Reference

Kotlin Docs

[Kotlin] Enhanced Loops

· 2 min read

In Kotlin, you can write much simpler and more convenient loops compared to Java. Let's see how you can use them.

1. .. operator

val fruits = listOf("Apple", "Banana", "Cherry", "Durian")

fun main() {
for (index in 0..fruits.size - 1) {
val fruit = fruits[index]
println("$index: $fruit")
}
}

Using .. creates a traditional loop that increments by 1.

2. downTo

val fruits = listOf("Apple", "Banana", "Cherry", "Durian")

fun main() {
for (index in fruits.size - 1 downTo 0) {
val fruit = fruits[index]
println("$index: $fruit")
}
}

Using downTo creates a loop that decrements as expected.

3. step

val fruits = listOf("Apple", "Banana", "Cherry", "Durian")

fun main() {
for (index in 0..fruits.size - 1 step 2) {
val fruit = fruits[index]
println("$index: $fruit")
}
}

With the step keyword, you can implement a loop that skips a specific number of elements. This also applies to downTo.

4. until

val fruits = listOf("Apple", "Banana", "Cherry", "Durian")

fun main() {
for (index in 0 until fruits.size) {
val fruit = fruits[index]
println("$index: $fruit")
}
}

Using until creates a loop that does not include the last number, eliminating the need for -1.

5. lastIndex

val fruits = listOf("Apple", "Banana", "Cherry", "Durian")

fun main() {
for (index in 0 .. fruits.lastIndex) {
val fruit = fruits[index]
println("$index: $fruit")
}
}

Now, using the lastIndex property, loops start to become easier to read. But of course, there's more to explore.

6. indices

val fruits = listOf("Apple", "Banana", "Cherry", "Durian")

fun main() {
for (index in fruits.indices) {
val fruit = fruits[index]
println("$index: $fruit")
}
}

indices returns the index range of the collection.

7. withIndex()

val fruits = listOf("Apple", "Banana", "Cherry", "Durian")

fun main() {
for ((index, fruit) in fruits.withIndex()) {
println("$index: $fruit")
}
}

By using withIndex(), extracting both index and value simultaneously simplifies the code, resembling Python's simplicity. This should be sufficient for most loop scenarios, but there's one more method left.

8. forEachIndexed

val fruits = listOf("Apple", "Banana", "Cherry", "Durian")

fun main() {
fruits.forEachIndexed { index, fruit ->
println("$index: $fruit")
}
}

Using a lambda function with forEachIndexed can make the code more concise, intuitive, and straightforward. Choose the appropriate method that suits your needs.


Reference

Kotlin Tips: Loops

Utilizing Ellipsoids on Earth with Kotlin

· 3 min read

Background

earth image reference1

Considering that the Earth is neither flat nor a perfect sphere, but an irregular ellipsoid, there is no perfect formula for quickly and accurately calculating the distance between two points at different longitudes and latitudes.

However, by using the geotools library, you can obtain mathematically corrected approximations quite easily.

Adding Dependencies

To use the Earth ellipsoid in geotools, you need to add the relevant library dependencies.

repositories {
maven { url "https://repo.osgeo.org/repository/release/" }
maven { url "https://download.osgeo.org/webdav/geotools/" }
mavenCentral()
}

dependencies {
...
implementation 'org.geotools:gt-referencing:26.2'
...
}

Writing Code

First, define the coordinates of Seoul and Busan as an enum class.

enum class City(val latitude: Double, val longitude: Double) {
SEOUL(37.5642135, 127.0016985),
BUSAN(35.1104, 129.0431);
}

Next, let's look at a simple usage example through a test code.

class EllipsoidTest {

@Test
internal fun createEllipsoid() {
val ellipsoid = DefaultEllipsoid.WGS84 // Creates an ellipsoid that is as close to the Earth as possible using the WGS84 geodetic system used in GPS

val isSphere = ellipsoid.isSphere // Determines if it is a sphere or an ellipsoid
val semiMajorAxis = ellipsoid.semiMajorAxis // Equatorial radius, the longer radius of the ellipsoid
val semiMinorAxis = ellipsoid.semiMinorAxis // Polar radius, the shorter radius of the ellipsoid
val eccentricity = ellipsoid.eccentricity // Eccentricity, indicates how close the ellipsoid is to a sphere
val inverseFlattening = ellipsoid.inverseFlattening // Inverse flattening value
val ivfDefinitive = ellipsoid.isIvfDefinitive // Indicates if the inverse flattening is definitive for this ellipsoid

// Orthodromic distance
val orthodromicDistance = ellipsoid.orthodromicDistance(
City.SEOUL.longitude,
City.SEOUL.latitude,
City.BUSAN.longitude,
City.BUSAN.latitude
)

println("isSphere = $isSphere")
println("semiMajorAxis = $semiMajorAxis")
println("semiMinorAxis = $semiMinorAxis")
println("eccentricity = $eccentricity")
println("inverseFlattening = $inverseFlattening")
println("ivfDefinitive = $ivfDefinitive")
println("orthodromicDistance = $orthodromicDistance")
}
}
isSphere = false
semiMajorAxis = 6378137.0
semiMinorAxis = 6356752.314245179
eccentricity = 0.08181919084262128
inverseFlattening = 298.257223563
ivfDefinitive = true
orthodromicDistance = 328199.9794919944

You can create an Earth ellipsoid with DefaultEllipsoid.WGS84. If you use SPHERE instead of WGS84, a sphere with a radius of 6371km will be created.

The distance result is in meters (m), so converting it to kilometers shows approximately 328km. If you search on Google, you may find 325km, so considering that there may be differences between the coordinates I chose and those chosen by Google, this is not a bad figure.

There are many other functions available as well. However, covering them all in this post would be too extensive, so if needed, I will cover them in another post.

info

The margin of error may not be satisfactory depending on business requirements, so before actual implementation, make sure to thoroughly test other methods in geotools.


Footnotes

  1. An Overview of SRID and Coordinate System

How to Use @JsonNaming

· 2 min read

Sometimes, the JSON naming conventions used in an API may differ from the naming strategy within your application.

{
"Title": "Frozen",
"Year": "2013",
"Type": "movie",
"Poster": "https://m.media-amazon.com/images/M/MV5BMTQ1MjQwMTE5OF5BMl5BanBnXkFtZTgwNjk3MTcyMDE@._V1_SX300.jpg",
"imdbID": "tt2294629"
}
private String title;
private String year;
private String imdbId;
private String type;
private String poster;

If the variable names do not match the JSON keys, the data will not be populated.

In such cases, you can use @JsonProperty(value) to map the data without changing the variable names in the project. However, if there are many fields with different naming strategies, using @JsonProperty(value) on each field can clutter the code with too many annotations.

This is where the @JsonNaming annotation comes in handy, allowing you to change the naming strategy of a class at once.

@JsonNaming

Before v2.12

You can elegantly solve this as follows:

@Data
@JsonNaming(value = PropertyNamingStrategy.UpperCamelCaseStrategy.class)
public class Movie {

private String title;
private String year;

@JsonProperty("imdbID") // Only where needed!
private String imdbId;
private String type;
private String poster;

}

image This method is deprecated and marked with a strikethrough.

However, this method has been deprecated since Jackson 2.12, so let's explore the newer approach.

After v2.12

Starting from version 2.12, you should use PropertyNamingStrategies.

@JsonNaming(value = PropertyNamingStrategies.UpperCamelCaseStrategy.class)

While a detailed explanation of the internal implementation may be too lengthy and off-topic, it is recommended to take a look as it is quite interestingly implemented!

info

In brief, the updated internal implementation involves an abstract class called NamingBase, which inherits the original PropertyNamingStrategy, and then the naming strategy inherits from NamingBase. NamingBase is used as a kind of intermediate implementation class.