Kotlin Problems and How to avoid them?

0
350
Kotlin problem and avoid them

Kotlin is all the rage lately. And while I do agree that the language is well thought out, it does have — as with everything else — its flaws.

As we already discuss about disadvantage of Kotlin but today in this article I’ll explain some of the Kotlin problems I encountered and try to help you avoid them.

You have to see : Advantage and why you should switch to Kotlin

Top 9 Kotlin problems with their solution:

1The mystery null

Image result for null

In Kotlin you can write your code as if null never existed and this can make you forget that null is omnipresent but it hides. Let’s look at this simple and seemingly innocent class:

class Foo {
    private val c: String
    init {
        bar()
        c = ""
    }
    private fun bar() {
        println(c.length)
    }
}

If you try to instantiate this, you’ll get a NullPointerException because bartried to access the length of c before it was initialized.

Of course the application logic was flawed here, but you still got an Exception. The worst part of this is that your IDE won’t complain about this.

The takeaway here is that Kotlin will help you in a lot of cases (nearly all) to avoid null, but you can’t forget about it and from time to time you’ll encounter things like this.

For every Kotlin developer : Top 20 Daily used Kotlin Code Snippet

2Handling nulls from the JDK

Image result for Handling nulls

Kotlin’s standard library handles nulls fine. But if you use classes from the JDK, you will have to handle possible null pointers from library functions by hand.

Most of the time the Kotlin classes are enough, but sometimes you have to use something like the ConcurrentHashMap:

val map = ConcurrentHashMap<String, String>()
map["foo"] = "bar"
val bar: String = map["foo"]!!

In this case, you have to use the !! operator. But in other cases the nullsafety operator (?) can also work. Nevertheless, if you use Java libraries extensively you’ll have to litter your code with !!s and ?s or write adapters for Java classes. This is something you can’t really avoid.

There’s another more hideous problem you might bump into. When using methods on JDK classes, they can return null and don’t have syntactic sugar like the Map access above.

You must see : 5 cool things you probably don’t know about Kotlin

Consider the following example:

val queue: Queue<String> = LinkedList()
queue.peek().toInt()

In this case, you use peek which in fact can return null. But the Kotlin compiler won’t complain so you can get a NullPointerException if your Queue was empty.

The problem here is that we used Queue which is a JDK interface and if you look at the implementation of peek:

/**
  * Retrieves, but does not remove, the head of this queue,
  * or returns {@code null} if this queue is empty.
  *
  * @return the head of this queue, or {@code null} if this queue is empty
  */
  E peek();

It says that peek will return E which will lead Kotlin to believe that E is not nullable. This might be worked around in a future version of Kotlin, but right now it is important to keep this in mind in your projects and use interfaces like this:

val queue: Queue<String?> = LinkedList()
queue.peek()?.toInt()

3The inner it

Related image

When a lambda has a single parameter you can omit it from your code and can use it instead:

“it: implicit name of a single parameter One other helpful convention is that if a function literal has only one parameter, its declaration may be omitted (along with the ->), and its name will be it.” — Kotlin docs

The problem with this is when you have nested functions like in this example:

val list = listOf("foo.bar", "baz.qux")
list.forEach {
    it.split(".").forEach {
        println(it)
    }
}

It only takes a few its to lose track which is which. The solution to this problem is to name the parameters explicitly:

list.forEach { item ->
    item.split(".").forEach { part ->
        println(part)
    }
}

That’s good now!!

Do you know :  What is Kotlin Kobalt build tool?

4The insidious copy

Take a look at this data class:

data class Foo(val bars: MutableList<String>)

Data classes give you a bunch of functions and you can also make a copy of them. Guess what this will print out:

val bars = mutableListOf("foobar", "wombar")
val foo0 = Foo(bars)
val foo1 = foo0.copy()
bars.add("oops")
println(foo1.bars.joinToString())

This will print foobar, wombar, oops. The problem is that while the name indicates that copy will make an actual copy in fact it will only copy the references in your object. This can be insidious if you forget to write an unit test and you pass your data classes around as if they were immutable value objects.

Advertisement

 

The solution to this problem is to pay attention to your data classes and if they should be value objects make them one:

val bars = listOf("foobar", "wombar")

data class Foo(val bars: List<String>)

There is an other problem with data classes: you can’t tell Kotlin which fields you want to put in your equals / hashCode, you can only overrideboth and write them by hand. Keep this in mind.

5Internal leakage

Take a look at this example:

class MyApi {
    fun operation0() {
    }
    internal fun hiddenOperation() {            
    }
}

If you use classes like this from other Kotlin projects the internal keyword will be respected. If you look at this from a Java project however hiddenOperation will be public! To avoid this I’d suggest using interfaces to hide implementation details:

interface MyApi {
    fun operation0()
}
class MyApiImpl: MyApi {
    override fun operation0() {
    }
    internal fun hiddenOperation() {
    }
}

6Non-generic global extensions

The utility of extension functions is unquestionably high, but with great power comes great responsibility. You can — for example — write extension functions to JDK classes which will be visible for the whole project. This can be problematic when they are non-generic and represent operations which only make sense in a local context:

fun String.extractCustomerName() : String {
    // ...
}

Now everybody on your project will scratch their heads when they bump into this. So I think it is good if you think twice before you write extension functions but they can be really powerful.

Start with Kotlin : Create Reddit like Android Kotlin app Step by Step

Here are some examples which might be useful:

/**
 * Returns an element of this [List] wrapped in an Optional
 * which is empty if `idx` is out of bounds.
 */
fun <T> List<T>.getIfPresent(idx: Int) =
        if (idx >= size) {
            Optional.empty()
        } else {
            Optional.of(get(idx))
        }
/**
 * Negates `isPresent`.
 */
fun <T> Optional<T>.isNotPresent() = isPresent.not()

7Unit returning lambdas vs Java SAM conversion

Image result for lambdas vs Java SAM

When you have functions which accept lambdas you can omit the returnkeyword if the lambda’s return type is Unit:

fun consumeText(text: String, fn: (String) -> Unit) {
}
// usage
consumeText("foo") {
    println(it)
}

 

Advertisement

 

This is fine but if you call this from Java you’ll face the awkward problem of needing to return Unit:

consumeText("foo", (text) -> {
    System.out.println(text);
    return Unit.INSTANCE;
});

This is very clunky from the Java side. If you try to make this work from Java you can define an interface:

interface StringConsumer {
    fun consume(text: String)
}
fun consumeText(text: String, fn: StringConsumer) {
}

then you can use Java’s SAM conversion to make this very simple:

consumeText("foo", System.out::println);

but then from the Kotlin side it becomes a mess:

consumeText("foo", object: StringConsumer {
    override fun consume(text: String) {
        println(text)
    }
})

The problem is that Kotlin does not support SAM conversion for Kotlin classes and it only works with Java classes. My suggestion is that for simple cases just use Java’s built in SAM interfaces like Consumer:

fun consumeText(text: String, fn: Consumer<String>) {
}
// usage from Kotlin
consumeText("foo", Consumer {
    println(it)
})
// usage from Java
consumeText("foo", System.out::println);

8Java interop with unmodifiable Collections

Kotlin gives you immutable variants of the JDK’s collection classes:

fun createSet(): Set<String> = setOf("foo")
// ...
createSet().add("bar") // oops, compile error

This is a very nice addition but if you look at this from the Java side you’ll see the JDK’s Set api:

createSet().add("bar"); // UnsupportedOperationException

If you try to modify this Set the same will happen as if you have used Java’s Collections.unmodifiableSet() method. I don’t know whether this can be (or should be) worked around but this is something you can keep in mind when working with Kotlin’s immutable versions of Java collections.

9No overloads in interfaces

This is only an issue from an interop perspective, but Kotlin does not support the @JvmOverloads annotation in an interfaceand you can’t use it on overrides either:

interface Foo {
    @JvmOverloads // OUCH!
    fun bar(qux: String)
}
class FooImpl : Foo {
    
    @JvmOverloads // YIKES!
    override fun bar(qux: String) {
    }
}

Currently the only thing you can do to have overloads is to define them by hand:

interface Foo {
    fun bar()
    fun bar(qux: String)
}

Keep it in mind though that you can suggest improvements to Kotlin itself using KEEP (Kotlin Evolution and Enhancement Process). KEEP is something like JEP in Java, but of course KEEP has much less red tape compared to JEP.

10Conclusion

Kotlin is a very popular language right now and I do agree that it is a TurboJava, but you should take any hype with a grain of salt. As we have seen above Kotlin problems which you should be aware of if you plan to use it.

All in all I think that the problems mentioned above can either be worked around easily or not critical and they don’t limit the usability of the language itself.

Share your thoughts

Loading Facebook Comments ...
Loading Disqus Comments ...