If you enjoy talks from 360 AnDev, please support the conference via Patreon!
In this talk, we will cover how you can get started with Kotlin. We will cover some best practices and things to avoid.
Introduction
My name is Christina Lee and I’m an Android developer at Pinterest. I’m Huyen Tue Dao and I’m an Android developer at Trello. We will cover Kotlin for people without Kotlin experience by live coding a sample app with common technologies such as Dagger and Retrofit.
Converting to Kotlin
First, convert a Java file to Kotlin file. If I examine the Java version of the code, I see that it was public abstract class. In Kotlin, you would not need to specify public
because it is the default.
Kotlin allows you to place things at a top level of a file. A note on access modifiers:
-
private
- means that the signature can only be seen in that particular class. -
internal
- anybody in the module that can see this class, can also see all internal members of the class. -
protected
- it’s private, but some classes can also see this field.
In Kotlin, abstract
is unchanged. If that abstract
class has members, they need to be implemented.
Extension (inheritance) is not noted with a “:”.
Primary and Secondary Constructors
Suppose an Animal
class has legs. The following would be an example of a primary constructor.
class Animal(legs: Int) {
constructor(legs: Int)
init {
}
}
In an instance that an animal may also have fur, I can use a secondary constructor:
class Animal(legs: Int) {
constructor(legs: Int, furType: String) : this (legs)
init {
}
}
Here, I will pass the secondary information to the primary constructor.
Classes
Scoping
class Animal(legs: Int) {
var legsPlusOne = legs + 1
init {
legs // in scope
}
}
In the above class, legs
, when assigning to the variable legsPlusOne
and in the init block, is in scope.
Note that if you write a function lower in the class, legsPlusOne
is in scope, but legs
is not.
class Animal(legs: Int) {
var legsPlusOne = legs + 1
init {
legs // in scope
}
fun someFun() {
legs // not in scope
legsPlusOne // is in scope
}
}
Nullability
Kotlin has the idea of a nullability type. It can be there, or it can also not be there and it can be null. If I remove or add a ?
, it determines whether I need to check for null, before working with the variable.
private lateinit var unbinder: Unbinder
If you ever think that you’re smarter than a compiler, you have this great opportunity to prove it by using the keyword lateinit
. Using this keyword tells the compiler, “I know that it looks like this isn’t initialized, but I can guarantee that before I use it, it will be.” But, if you are wrong, it will crash.
Handling Nullability
In onCreateView
, all of the parameters are marked nullable.
override fun onCreateView(inflater: LayoutInflator?,...)
Anytime you have something that’s marked nullable, you have to handle both cases, which results in a different branch and adds code complexity. You may want to remove question marks to simplify your code, but in some cases, it would cause it to no longer compile, as the signature no longer matches.
A bad null handling policy is this:
inflator!!.context
Here, if the inflator is null, it will throw a NullPointerException if it is in fact null.
A better one is this:
inflator?.context
This second one is better because it gives you a chance to avoid a NullPointerException.
Lambda
Lambda is a function literal. It allows you to define a function as an expression that you can pass to other methods. We have a retry button, with a familiar setOnClickListener
, and rather than instantiating and passing a ClickListener, we’re passing it a lambda, which contains all of our click button behaviors.
A Kotlin Lambda will have braces. First, it will contain a list of parameters that go to your function/lambda. As we are in Kotlin world, the way that the parameters are defined are name first, and then type second. And then you’ll see this right arrow. And then you’ll have the body of your function, the actual behaviors.
retryButton.setOnClickListener { v ->
v.visibility = View.INVISIBLE
load()
}
When you have a lambda that has a single parameter, you can ask Kotlin to implicitly declare it for you.
Companion object
Kotlin does not have static variables. Instead, Kotlin has a companion object, which allows you to do static variables. Within the object, you can declare properties and methods that you can call in static-like ways.
companion object {
private val SPAN_COUNT = 2
private val PERMISSIONS_REQUEST_INTERNET = 100
}
If I wanted to call something in my companion object in other Kotlin code, I might write UnsplashListFragment.SPAN_COUNT, to achieve a static-like behavior in our Kotlin.
Boxed, Unboxed Arrays
In Kotlin, there are many types of arrays - e.g. boxed arrays.
The difference between boxed and unboxed arrays is straightforward. Boxed arrays are arrays with boxed types. Unboxed arrays are arrays of primitive types.
A Caution
Converting Java to Kotlin will result in code that is syntactically correct, but will result in it being more difficult to read.
For example, here, I have a network request with a success and error callback. They’re not lined up, and it almost looks like they’re in two different places.
It’s disjointed and hard to understand.
About the content
This talk was delivered live in July 2017 at 360 AnDev. The video was recorded, produced, and transcribed by Realm, and is published here with the permission of the conference organizers.