6 magic sugars that can make your Kotlin codebase happier — Part 2
Try new things. I think a lot of what people call intelligence boils down to curiosity. — Aaron Swartz
We are continuing our journey through some of my favourite Kotlin constructions. In the first part, you learned how to use
sealed classes and permute two or even three enums in an elegant and concise way.
In this part, I would like to show you something that I frequently use in components that have a presentation layer. There will be also something special that perfectly demonstrates how to use an
reified function. I hope you will appreciate my sugars the same way I do.
Let’s start with the well-known
with() function to scope invocations
Assuming that you have never used a
with() function before let’s see what you can find in the documentation:
Calls the specified function block with the given receiver as its receiver and returns its result.
😕😢😭… At first glance it looks a little complicated, but only if you have no idea what a block or receiver is. As always, an example will clarify them all for you:
The first line is a no-brainer. It shows the receiver of the type
String. The receiver type is important because in line three you can see a definition of the function literal with a specified receiver object. This notation may remind you of an extension function mechanism, and that is a good thing. Inside the specified block, you can call methods on the receiver object without any qualifiers. The last interesting thing is the return
Unit type of the block function.
Now, block and receiver should not have any hidden logic. Using your new knowledge, applying block and receiver to the
with() function is not only an easy task but also fun.
Let’s make it a little shorter, more concise and typeless.
The block lambda is the last parameter of the
with() function and because of that, you can place it outside the round brackets. That is it!
So, how can you apply a
with() function to your codebase to make it happier? During the last couple of months, I often used it to omit qualifiers like
view.hide() in the Presentation layer of my UI components:
That looks nice, doesn’t it? Moreover, it is really easy to work with such code in the future. There is no need to think about the
view because our function is already scoped with it.
Now, it is time to go further and write your own
withCorrectType() function using the
Try inline reified to invoke
I truly love code reviews. In my opinion, it is one of the best ways to learn fast and exchange knowledge in your team. The next sugar is taken from my teammate’s comment to code that I refactored lately. Have you ever had the feeling that something was wrong with the codebase but you did not know how to make it better? If so, don’t feel bad because it’s completely normal.
Let’s analyse the following.
Can you point out any weak lines in this code? It is quite obvious that
IconItemRenderer have similar logic in the
render() method. Additionally, you can use the knowledge from the previous sugar and omit
view qualifiers. Easy!
Now it is time to come up with something clever. How about creating a function similar to
with() and trying to move the type-checking code there?
Excellent! Now you are good to go and refactor your
render() function, aren’t you? Hmmm… There is a small problem because your code does not compile.
What does it even mean that it is not possible to check for an instance of the erased type: T? This error is strictly limited to the erasure of generic types and the way generics work.
During the type erasure process, the Java compiler erases all type parameters and replaces each with its first bound if the type parameter is bounded, or Object if the type parameter is unbounded. — docs.oracle.com
So, is it possible to prevent your generic
T from being erased? With Kotlin, everything is possible! ...I mean, almost everything😜. By using a combination of
reified keywords, you can easily fix your problem.
You can go deeper and use the IntelliJ IDEA Kotlin Bytecode tool to find out what the Kotlin compiler did with your
reified type and how the
inline keyword helps.
That is pure magic, right? The Kotlin compiler left your type because you marked it as
reified. This would not be possible without marking your function using
inline, as the code from
withCorrentType() had to be injected directly into the invocation places.
Summing up, the inlining function with a
reified type not only makes your code more readable, it also has no negative impact in terms of performance😎.
That’s all for the second part, but if you’re still hungry, stay tuned for Part 3, or return to Part 1.
If this was interesting to you, please give some claps 👏 or let me know on Twitter.