Kotlin Common tips for developers

abhishek1511991

Abhishek Srivastava

Posted on January 14, 2023

Kotlin Common tips for developers

1. What is difference between value class and inline class

Value classes are value-based classes that wrap primitive values. As of now, Kotlin value classes only support wrapping one single primitive value and it provide type safety. However, wrapping a primitive type can be very costly in terms of performance.

Value classes deceleration might look pretty similar to data classes. The signature looks exactly the same, except that instead of data class the keyword is value class


@JvmInline
value class DisplayBrightness(val value: Double)

fun setDisplayBrightness(newDisplayBrightness: DisplayBrightness) { ... }

Enter fullscreen mode Exit fullscreen mode

You might have noticed the @JvmInline annotation. In short, while the Kotlin/Native and Kotlin/JS backends would technically support value classes with more than one field, Kotlin/JVM currently does not. This is because the JVM only supports its built-in primitive types. so they needed to find a temporary compilation strategy that can be employed now. In order to make this obvious, @JvmInline is enforced for the time being.

Behind the scenes, the compiler treats value classes like a type alias — with one major difference: Value classes are not assignment-compatible, meaning that this code will not compile:

@JvmInline
value class DisplayBrightness(val value: Double)

fun setDisplayBrightness(newDisplayBrightness: DisplayBrightness) { ... }

fun callingFunction() {
    val weight: Double = 85.4
    setDisplayBrightness(weight) // 💥
}

Enter fullscreen mode Exit fullscreen mode

Inline classes have some, more or less apparent, restrictions though: It’s required to specify precisely one property in the primary constructor, as shown with value. You can't wrap multiple values in one inline class. It is also prohibited to have init blocks in inline classes, and you cannot have properties with backing fields. Inline classes can have simple computable properties.

At runtime, the wrapped type of an inline class will be used without its wrapper whenever possible. This is similar to Java’s boxed types like Integer or Boolean, which will be represented as their corresponding primitive type whenever the compiler can do that. That exactly is the great selling point for inline classes in Kotlin: When you inline a class, the class itself won't be used in the byte code unless it's absolutely necessary. Inlining classes drastically reduces space overhead at runtime.

inline class Password(val value: String)
inline class UserName(val value: String)

fun auth(userName: UserName, password: Password) { println("authenticating $userName.")}

fun main() {
    auth(UserName("user1"), Password("12345"))
    //does not compile due to type mismatch
    auth(Password("12345"), UserName("user1"))
}

Enter fullscreen mode Exit fullscreen mode

2. What does ‘by’ keyword do in Kotlin

Kotlin supports delegation of design pattern by introducing a new keyword “by”. Using this keyword or delegation methodology, Kotlin allows the derived class to access all the implemented public methods of an interface through a specific object.

In simple words, you can understand by keyword as provided by.

From the perspective of property consumer, val is something that has getter (get) and var is something that has getter and setter (get, set). For each var property there is a default provider of get and set methods that we don't need to specify explicitly.

But, when using by keyword, you are stating that this getter/getter&setter is provided elsewhere (i.e. it's been delegated). It's provided by the function that comes after by.

So, instead of using this built-in get and set methods, you are delegating that job to some explicit function.

One very common example is the by lazy for lazy loading properties. Also, if you are using dependency injection library like Koin, you'll see many properties defined like this:

var myRepository: MyRepository by inject()  //inject is a function from Koin

Enter fullscreen mode Exit fullscreen mode

In the class definition, it follows the same principle, it defines where some function is provided, but it can refer to any set of methods/properties, not just get and set.

class MyClass: SomeInterface by SomeImplementation, SomeOtherInterface

Enter fullscreen mode Exit fullscreen mode

This code is saying: 'I am class MyClass and I offer functions of interface SomeInterface which are provided by SomeImplementation. I'll implement SomeOtherInterface by myself (that's implicit, so no by there).'

3. What does ‘*’ in Kotlin when used within <>

Star projection, denoted by the * character may be helpful to think of the star projection as a way to represent not just any type, but some fixed type which you don’t know what is exactly.

For example, the type MutableList<*> represents the list of something (you don't know what exactly). So if you try to add something to this list, you won't succeed. It may be a list of Strings, or a list of Ints, or a list of something else. The compiler won't allow to put any object in this list at all because it cannot verify that the list accepts objects of this type. However, if you attempt to get an element out of such list, you'll surely get an object of type Any?, because all objects in Kotlin inherit from Any.

4. What does ‘*’ in Kotlin when used with list iteration

The * operator is known as the Spread Operator in Kotlin.

When we call a vararg-function, we can pass arguments one-by-one, e.g. asList(1, 2, 3), or, if we already have an array and want to pass its contents to the function, we use the spread operator (prefix the array with *):

It can be applied to an Array or with any collection before passing it into a function that accepts varargs.

If you have a function that accepts a varied number of arguments…

fun sumOfNumbers(vararg numbers: Int): Int {
    return numbers.sum()
}

Enter fullscreen mode Exit fullscreen mode

Use the spread operator to pass an array’s elements as the arguments:

val numbers = intArrayOf(2, 3, 4)
val sum = sumOfNumbers(*numbers)
println(sum) // Prints '9'

Enter fullscreen mode Exit fullscreen mode

Notes:

  • The * operator is also the multiplication operator (of course).
  • The operator can only be used when passing arguments to a function. The result of the operation cannot be stored since it yields no value (it is purely syntactic sugar).
  • The operator may confuse some C/C++ programmers at first because it looks like a pointer is being de-referenced. It isn’t; Kotlin has no notion of pointers.
  • The operator can be used in-between other arguments when calling a vararg function. This is demonstrated in the example here.
  • The operator is similar to the apply function in various functional programming languages.

5. Kotlin’s difference between vararg and spread operator

vararg in Kotlin
Kotlin also supports declaring a function that can have a variable number of arguments. You can do that by prefixing parameter name with the vararg modifier: fun format(format: String, vararg args: Any)

Rules: In Java, the vararg parameter has to be the last one in the parameters list — so you can only have one vararg parameter. While in Kotlin, the vararg parameter doesn’t have to be the last one in the list, multiple vararg parameters are still prohibited.

Now let’s see how the decompiled corresponding Java source code looks like when declaring vararg parameters.

Declaring vararg param as the last one in list, in a Kotlin function, looks like this:

fun format(format: String, vararg params: String)

Enter fullscreen mode Exit fullscreen mode

and will be compiled into the corresponding Java code:

void format(@NotNull String format, @NotNull String... params)

Enter fullscreen mode Exit fullscreen mode

Spread operator
Kotlin’s spread operator — the operator that unpacks an array into the list of values from the array. It is needed when you want to pass an array as the vararg parameter.

If you try to call format function defined above like this:

val params = arrayOf("param1", "param2")
format(output, params)

Enter fullscreen mode Exit fullscreen mode

it will fail with Type mismatch: inferred type is Array but String was expected compilation error.

To fix this, you need to use spread operator to unpack params array into the corresponding values: format(output, *params)

Let’s see how the corresponding decompiled Java code looks like in this case:

String[] params = new String[]{"param1", "param2"};
format(output, (String[])Arrays.copyOf(params, params.length))

Enter fullscreen mode Exit fullscreen mode

When using the spread operator, you can pass additional values to the vararg parameter — or even combine multiple spread arrays in the same parameter:


val params = arrayOf("param1", "param2")
val opts = arrayOf("opts1", "opts2", "opts3")
format(output, *params, "additional", *opts)

Enter fullscreen mode Exit fullscreen mode

6. Difference between “fold” and “reduce” in Kotlin

fold() and reduce() are two different methods, both are helpful for traversing a collection. We will see how and when to use these two methods.

fold()
If we want to traverse through a collection serially, then we can use fold().

  • fold() takes one initial value and one operation to perform the operation on the initial value.
  • There are different kinds of fold(), for example, foldRight() folds from right to left. By default, fold() will traverse from left to right. The following example demonstrates how to use fold() to traverse through a collection.
fun main(args: Array<String>) {
val MyTotal = listOf(1, 2, 3, 4, 5).fold(0) {
      initialValue, result -> result + initialValue
   }
   print("The sum of list members: " +MyTotal)
}

Enter fullscreen mode Exit fullscreen mode

Output : fold() will start the operation from the 0th index and it will end its operation and store the final value in MyTotal.

The sum of list members: 15

Enter fullscreen mode Exit fullscreen mode

reduce()
reduce() is one of the default methods in Kotlin that helps to convert a given set of collection into a single set output result. reduce() is one of the built-in functions that can be applied on a given collection. It takes the first and second elements as the operation argument and this is the main difference between reduce() and fold().

fun main(args: Array<String>) {
   val x = listOf(1,2,3,4,5,6,7,8,9)
   val y = x.reduce { y, vars -> y + vars }
   println("The value of reduce() ---> "+y)
}

Output: The value of reduce() ---> 45

Enter fullscreen mode Exit fullscreen mode

Thanks for reading…

💖 💪 🙅 🚩
abhishek1511991
Abhishek Srivastava

Posted on January 14, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related