80

6 magic sugars that can make your Kotlin codebase happier — Part 2

 6 years ago
source link: https://medium.com/grand-parade/6-magic-sugars-that-can-make-your-kotlin-codebase-happier-part-2-843bf096fa45
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
1*a9ZzWZ1DU4gP793MC8CQTQ.jpeg

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 inline reified function. I hope you will appreciate my sugars the same way I do.

Let’s start with the well-known with() function.

Use the 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:

inline fun <T, R> with(receiver: T, block: T.() -> R): R (source)

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.show() and 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 reified keyword👊!

Try inline reified to invoke withCorrectType()

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 MediaItemRenderer and 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.

1*PPjnUySn2ragXCvPf_BSIg.png

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 inline and reified keywords, you can easily fix your problem.

Refactored code.

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.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK