DEV Community

 ViksaaSkool
ViksaaSkool

Posted on

2 1

So, Kotlin, ha?

kotlin

This blog post (originally titled “Late for the Kotlin party”) has been in the file cabinet since spring and the “see the light of the optical cable” moment was postponed, because of, reasons. By now the original title has become outdated, but I kept most of the original structure and I think that the advices and notes in the post will work well for newcomers, so let it be.

You’re an Android developer and you’ve been slacking off learning Kotlin, staying in the comfort zone, maintaining Java code project(s), recently started Android and Java was overwhelming enough, or whatever the reason is - and now you’re figuring out that you’re kinda late? Well I guess so was I . This blog post is result of my Kotlin learning woes that I've structured in form of "Kotlin notes”/”point board”.
It is what it is, you have to do it if you want to stay relevant and even if your current position might not require that from you, things might change and you might end up in this position. Buckle up, I’ve made a crash transition course. There are a lot of layers to this, but it’ll get you started.

buckle up

Let’s start with the resources, there are way too much resources and I’ll try to keep as tidy as possible:

  1. Kotlinlang docs - I know this is not the advice to give in the age of “googling everything”, but try to memorize as much as possible – like you’re studying for exam, a lot of things will be new and they might not have context but with trying out stuff and looking at examples the pieces will fall together. You won’t be able to memorize everything and you’ll need to come back from time to time, but I’ve found this approach very helpful and having those “aha, so that’s what they’ve meant with that/so that’s how I use this-and-this-thing” moments To be noted, also: official Android documentation – as it comes to examples that matter you. You’ll come here when troubles arise just to have reality check that you’re not that smart and that component you thought was intuitive and you knew how it worked does not quite resemble the reality.
  2. Blogs – dev.to/medium posts from enthusiast from the industry are probably the best source where you can learn new stuff – they are usually targeted and are result of some difficulty that was overcame by someone patient enough to derivate the effort into tutorial/github project/”copy – paste code”.

  3. Books – there are couple books, but the one that I think cuts it – is "Kotlin for Android Developers - Learn Kotlin the easy way while developing an Android App" by Antonio Leiva and if you have the means I recommend you get your hands to it. Dependent on your level will be the level of how useful you’ll find it (ideally you’ll create the app along reading, or just read it and try to replicate some of the code and concepts into your own idea/s), still be open there are always chances to learn new stuff.

  4. r/androiddev/ - stay up-to-date everything including Kotlin – read it once or twice a week. Or daily, with the coffee mug. Great community that can also help with some issues and point out for you other learning resources.

  5. When you go trough the resources and get a bit knowledge on the matter you'd like to see examples and how the pros are doing it. This article points out 10 Android apps that are written in Kotlin and are open source on Github, I suggest you check them out a little bit latter, when you start coding in Kotlin and get stuck or look for ideas to solve something.

First thing you hear when Kotlin witness knocks on your door is that NullPointerException doesn’t exist anymore and your life will get easier. Yeah, you also need to learn a bunch of new stuff and there are A LOT of new stuff. I know, I know, like every good programming story starts, this one also starts with a lie - that it’ll take you just few days. Amm, no. It’s different from Java and no matter what they say, it takes time (effort) to get familiar with the new terms, paradigms and then to know when to apply{} them (needless to say, pun intended). Yes, you can read Kotlin code even if you never written one line.

Goodbye NullPointerException, everything will work perfectly now

Everything starts with Any. Here are the basics:

/* Declaring variables */
//[val/var] name_of_variable : [type] = value
//no need for ; at the end of the line
val a: Int = 1
var b: String = "NullPointerException is dead"
var c: Double? = 0.4
//val - read only value, constant
//var - mutable variable
//the '?' after the type means that c can also be null, if this is not added then c won't be able to get null as value
//Built-in types types: Int, Long, Double, Short, Float, Long
//Also: String, Boolean, Char
val x: String? = "Null Safety"
x.length // won't compile
x?.length // will compile
val y: String = null // won't compile
/* Literals and expressions */
val i = 10 //don't have to explicitly state :Int
println("i = $i") // prints "i = 10"
val s = "kotlin"
println("$s.length is ${s.length}") // prints "abc.length is 6"
//best use: for logging and assiging value when fetching from servise or some data source
/* Expressions */
//replace the switch with 'when'
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> { // Note the block
print("x is neither 1 nor 2")
}
}
//the result of if/else block can be directly assigned to val/var
val max = if (a > b) a else b
//Ranges defined as in [range_start]..[range_end]
if (i in 1..10) { // equivalent of 1 <= i && i <= 10
println(i)
}
//ranges and iteration
for (i in 1..3) {
println(i) //1,2,3
}
/* Break and Continue Labels */
//Any expression in Kotlin may be marked with a label. Labels have the form of an identifier followed by the @
fun foo() {
listOf(1, 2, 3, 4, 5).forEach lit@{
if (it == 3) return@lit // local return to the caller of the lambda, i.e. the forEach loop
print(it)
}
print(" this point is reachable, done with explicit label") // if it wasn't for the lit@, this line in the code
//wouldn't have been reachable
}
//to be honest, I don't think there's market for this, but people always will find a way to fancy up their code.
/* Functions */
//[fun]function_name(argument:ArgumentType):returnType{} or =
//if void just ommit the return type or Unit
fun double(x: Int) = x * 2
//same as
fun double(x: Int):Int{return x * 2}
//for variable number of arguments 'vararg' modifier is used
fun <T> asList(vararg ts: T): List<T> {
val result = ArrayList<T>()
for (t in ts) // ts is an Array
result.add(t)
return result
}
/* Classes */
//constructor is in the declaration of the class, which is known as primary constructor and cannot contain any code,
//Initialization can be placed in blocks that are prefixed with 'init'
//by default all classes are public if not specified
class Customer(name: String) {
init{
val customerKey = name.toUpperCase()
}
}
//secondary constructor - has to be noted with the 'constructor' keyword, has to delegate the primary constructor
//either directly or indirectly
class Customer(name: String) {
init {
val customerKey = name.toUpperCase()
}
constructor(name: String, surname: String, age: Int) : this(name){
println("customer $name $surname, aged $age is always right")
}
}
//also it can be done like this
class MyView : View {
constructor(ctx: Context) : super(ctx)
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}
//Inheritance is done in the following manner
open class Base(p: Int)
class Derived(p: Int) : Base(p)
/* Instancing object - no 'new' keyword required */
val rectangle = Rectangle(5.0, 2.0)
//this means that you'll have to bend the rules when instancing new click listener with ->
//object:[the object implementation]
var view.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
toast("Kotlin is the best")
}
})
/* Class properties */
class Address {
private val LOG_TAG: String = Address::class.java.simpleName
var name: String = "Jon"
var street: String = "Malkovich"
var city: String = "Copenhagen"
}
//properties and fields are by default public, no need to use getters and setters - yes I know it's more complex than that,
//just believe me.
//private - means visible inside this class only (including all its members);
//protected — same as private + visible in subclasses too;
//internal — any client inside this module who sees the declaring class sees its internal members;
//public — any client who sees the declaring class sees its public members.
/* Interfaces */
//nothing special regarding the interfaces in Kotlin compared to Java
interface MyInterface {
fun bar()
fun foo() {
// optional body
}
}
class Child : MyInterface {
override fun bar() {
// body
}
}
view raw KotlinBasics.kt hosted with ❤ by GitHub

This doesn't look so bad right? Let's go with more complex subjects, remember those Utils classes you’ve been copy-pasting from project to project? Well, you don’t need to now, you can replace them with extensions for the most part or change the structure a bit. To be noted, some more interesting concepts like lambdas (that have been around), inline functions are introduced that will yield the wanted result - making modular, reusable code snippets and blocks, happen with less work. These concepts come along with a lot of baggage and there's a steep learning curve since some of the examples might look too abstract and then real life app usage might seem too complex and hard to understand. I'll just throw them out there, yet it'll take time and effort to be able to know and understand when to use them. Probably the easiest and most intuitive part of the code when you come across these concept is when you build your util classes or when you handle events and callbacks. Start from there and build from that.

//A higher-order function is a function that takes functions as parameters, or returns a function. kotlin has the power
//to pass around functions
/* Lambdas */
//by definition, lambda expressions are functions that are not declared, but passed immediately as an expression
//surrounded by curly braces and the body goes after ->
//very basic definition
val sum = { x: Int, y: Int -> x + y }
//lambdas can be passed as parameter
val product = items.fold(1) { acc, e -> acc * e }
//if variables are unused, underscore is added (by the editor)
map.forEach { _, value -> println("$value!") }
//if lambdas have only one parameter, which is the common practice it's called "it", it that case there's no need to use the -> syntax
//and just use "it"
ints.filter { it > 0 } // this literal is of type '(it: Int) -> Boolean'
// it is an extension function
// it send this as it’s argument.
// it returns this (i.e. itself)
//another thing about the lamdas is that you can specify the return, i.e. use qualified return syntax - @return type
ints.filter {
val shouldFilter = it > 0
return@filter shouldFilter
}
//anonymous function - very much like a regular function declaration, except that its name is omitted
fun(x: Int, y: Int): Int = x + y
/* Function types */
//Kotlin uses a family of function types like (Int) -> String for declarations that deal with functions:
//1. All function types have a parenthesized parameter types list and a return type: (A, B) -> C denotes a type
//that represents functions taking two arguments of types A and B and returning a value of type C
//2. Function types can optionally have an additional receiver type,
//which is specified before a dot in the notation: the type A.(B) -> C represents
//functions that can be called on a receiver object of A with a parameter of B and return a value of C
//3.Suspending functions belong to function types of a special kind, which have a suspend modifier in the notation,
//such as suspend () -> Unit or suspend A.(B) -> C
/* Inline functions */
//inline function will be substituted by its code during compilation, instead of doing the real call to a function.
//It will reduce memory allocations and runtime overhead in some situations - inline functions will substitute
//the code of the function in the place where it is called, so it won’t require an internal object for that.
inline fun <T> lock(lock: Lock, body: () -> T): T { ... }
//example
inline fun supportsLollipop(code: () -> Unit) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
code()
}
}
//use the function as
supportsLollipop {
window.setStatusBarColor(Color.BLACK)
}
/*Example for extensions*/
//instead of using util methods, predefined methods can be redefined/extended to suit your needs
//transit to another Activity
fun Activity.callTo(clazz: Class<out Activity>) {
startActivity(Intent(this, clazz))
}
//add and replace fragment using inline functions
inline fun FragmentManager.inTransaction(func: FragmentTransaction.() -> Unit) {
val fragmentTransaction = beginTransaction()
fragmentTransaction.func()
fragmentTransaction.commit()
}
fun AppCompatActivity.addFragment(fragment: Fragment, frameId: Int, backStackTag: String? = null) {
supportFragmentManager.inTransaction {
add(frameId, fragment)
backStackTag?.let { addToBackStack(fragment.javaClass.name) }
}
}
fun AppCompatActivity.replaceFragment(fragment: Fragment, frameId: Int, backStackTag: String? = null) {
supportFragmentManager.inTransaction {
replace(frameId, fragment)
backStackTag?.let { addToBackStack(fragment.javaClass.name) }
}
}
/* Utils classes */
//util methods and classes can still be used and are relevant concept in a lot of cases
//anotate the static fun and definie it in companion object inside of the class
companion object{
@JvmStatic
fun debugToastMessage(context: Context, message:String, duration:Int){
if(BuildConfig.DEBUG){
Toast.make(context, message, duration).show()
}
}
}

Let’s dwell into something you’ll come across and use frequently – high-order functions that implement idiomatic patters – apply, let, also and run/with. The concept is introduced in the standard Kotlin library and they are meant to help the developer achieve more with writing less – a very rough, Java-resembling explanation of the functions is that they are “enhancement” of this.

/* With */
//receives an object, and a function that will be executed as an extension function, which means that with can be used
//inside of a function to refere to the object, i.e. do something "with"
inline fun <T, R> with(receiver: T, f: T.() -> R): R = receiver.f()
//example
fun getPersonFromDeveloper(developer: Developer): Person {
return with(developer) {
Person(developerName, developerAge)
}
}
/* Apply */
//very similar to with and it's mainly used to aviod using Builders and Builder pattern - the function can initialize itself
//the way it needs and return the same object
inline fun <T> T.apply(f: T.() -> Unit): T { f(); return this }
//example
fun getDeveloper(): Developer {
return Developer().apply {
developerName = "Amit Shekhar"
developerAge = 22
}
}
/* Also */
//maybe the most intuitive of all of them - calls specified function block with the current value (this) as its argument and
//returns this value - when something additionaly needs to be done (in specified circumstances)
inline fun <T> T.also(block: (T) -> Unit): T {block(this); return this}
//example
val person: Person = getPerson().also {
print(it.name)
print(it.age)
}
/* Let */
//simple function that can be called by any object. It receives a function that will take the object as a parameter, and returns the value that this function
//returns. It is really useful to deal with nullable objects for instance. This is the definition
inline fun <T, R> T.let(f: (T) -> R): R = f(this)
//example
File("a.txt").let {
// the file is now in the variable "it"
it.delete()
}
/* Run */
//calls the specified function block and returns its result - can be with or without value as its receiver and returns its result
inline fun <R> run(block: () -> R): R {block()}
//or
inline fun <T, R> T.run(block: T.() -> R): R {block()}
//example - retrieve if person in inserted in the db in a run block
val inserted: Boolean = run {
val person: Person = getPerson()
val personDao: PersonDao = getPersonDao()
personDao.insert(person)
}

This table sums it all up and how time passes and you get experience, it’ll become more and more apparent:

let apply it

Yes, making decisions is hard:

decision making is hard

On this subject, here’s a great blog post that you should read.

Another thing that's specific to Kotlin are Delegated properties, based on the delegation pattern, which by definition is an object-oriented design pattern that allows object composition to achieve the same code reuse as inheritance. The explanation in Android terms can be simplified as value assignment to property under certain conditions.Delegates can be standard from Kotlin and custom, defined by the developer. In the following gist, more details:

//we have class called Delegate that defines how to get and set values
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, thank you for delegating '${property.name}' to me!"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$value has been assigned to '${property.name}' in $thisRef.")
}
}
//In order to implement the delegate you need to add it after "by"
class Example {
var p: String by Delegate()
}
//this means get and set are defined as in Delegate class, other classes can reuse this assignment definition, a.k.a Deletage
//Standard delegates:
/* Lazy */
//function that takes a lambda and returns an instance of Lazy<T> which can serve as
//a delegate for implementing a lazy property: the first call to get() executes the
//lambda passed to lazy() and remembers the result, subsequent calls to get() simply return the remembered result.
//By default, the evaluation of lazy properties is synchronized: the value is computed only in one thread,
//and all threads will see the same value.
val lazyValue: String by lazy {
println("computed!")
"Hello"
}
fun main() {
println(lazyValue)
println(lazyValue)
}
/* Observable */
//Delegates.observable() takes two arguments: the initial value and a handler for modifications.
//The handler gets called every time we assign to the property (after the assignment has been performed).
//It has three parameters:
//property being assigned to
//old value
//new value
class User {
var name: String by Delegates.observable("<no name>") {
prop, old, new ->
println("$old -> $new")
}
}
fun main() {
val user = User()
user.name = "first"
user.name = "second"
}
/* Lateinit */
//identifies that the property should have a non-nullable value, but its assignment will be delayed. If the value is requested
//before it is assigned, it will throw an exception that clearly identifies the property being accessed.
class App : Application() {
companion object {
lateinit var instance: App
}
override fun onCreate() {
super.onCreate() instance = this
}
}

Regardless of the language, you should use generics and more importantly you need to know when and why to use them. By trying to make your code look fancy you might end up over-engineering the whole damn thing, but that's a general rule and subject out of the scope of this blog post. Some simple examples how generics are done in Kotlin:

//One very powerful example of Generics might in combination with delegates is creation of SharedPreferences support
//taken from Kotlin for Android Developers by Antonio Leiva
class MyKotlinSharedPreference<T>(val context: Context, val name: String, val default: T) {
val prefs by lazy {
context.getSharedPreferences("default", Context.MODE_PRIVATE)
}
//operator overloading
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
return findPreference(name, default)
}
//operator overloading
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
putPreference(name, value)
}
//use when yo distingush type
private fun <T> findPreference(name: String, default: T): T = with(prefs) {
val res: Any = when (default) {
is Long -> getLong(name, default)
is String -> getString(name, default)
is Int -> getInt(name, default)
is Boolean -> getBoolean(name, default)
is Float -> getFloat(name, default)
else -> throw IllegalArgumentException("This type can't be saved into Preferences")
}
res as T
}
//use when yo distingush type
private fun <U> putPreference(name: String, value: U) = with(prefs.edit()) {
when (value) {
is Long -> putLong(name, value)
is String -> putString(name, value)
is Int -> putInt(name, value)
is Boolean -> putBoolean(name, value)
is Float -> putFloat(name, value)
else -> throw IllegalArgumentException("This type can be saved into Preferences")
}.apply()
}
}

One new thing that has been out there for a while, but now with Kotlin is introduced in Android are Coroutines. The main purpose of Couroutines is to tak over the async/background-running parts of the app and make them smoother and nicer. That's in theory. There are few resources on this and even though rock star part of the community has already started using them, it might be too early for you to implement these in more serious project. Here are the basics

You should take into account other subjects that are complex regardless of the language, but do have some specifics, like implementing patterns and when to use them. Architectures that are popular and have pretty detailed explanation and examples can be found here:

Watch out when you implement Dagger. Things get funky and you need to use some of the things discussed in the gists above in order to make it work. The official coffee maker documentation is not very intuitive, so for more here and here (alternative: Koin, with caution).

Very popular Kotlin library that makes Android development easier - Anko. Suggestion - don't use it until you get grasp of things.

Last, but not least - testing - a bit different, but that is such a broad subject that will probably be touched upon in some other blog post in the near future.

tl;dr -> How is Kotlin different from Java:

  • New syntax, obviously - funky (and useful stuff)
  • No NullpointerException (by default)
  • String Literals
  • Companion objects
  • Delegates (lateinit, by lazy)
  • Lambdas (are everywhere)
  • let, apply, also, it, when, run - rough explanation: enhancements of "this."
  • Overriding properties
  • Data classes (no more getters and setters)
  • Sealed Classes
  • Inline classes
  • Extensions (say goodbye to ___Utils.class)
  • Inline functions
  • Function types
  • Functions with variable number of arguments (Varargs)
  • Operator overloading
  • Coroutines

To wrap it up on a Zen note, to self and whoever got to here by reading: you can’t use everything at once and you can’t know everything. Some things that are hot and all the cool kids are using them, are going to get extinct before you know it. Others are here to stay. In a sense everything in the industry is hype, things are fast changing (and evolving) and you should adopt to the changes fast. Only a fool can think that Kotlin is gonna disappear anytime soon, so all feelings aside, this is (and will be) the way to go when it comes to Android development.

good luck

P.S. This tutorial (in form of workshop) from Yelp's GitHub is just awesome!

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read full post →

Top comments (0)