From Punched Cards to Prompts
AndroidIntroduction When computer programming was young, code was punched into cards. That is, holes were punched into a piece of cardboard in a format...
Google surprised many at its 2017 I/O keynote by introducing Kotlin as a first-party language.
This day has been long-awaited at Big Nerd Ranch—we’ve been writing Kotlin apps internally and have fallen in love with its concise syntax and safety.
Now that Kotlin has the official seal of approval, let’s talk about what it can do for you.
Kotlin is a JVM language developed at JetBrains, reaching 1.0 in February of 2016.
Up to this point, it’s primarily gathered steam in the Android community, providing relief to developers whose language growth had stagnated.
The first question people often ask about Kotlin’s introduction as an official language is, “Is this Android’s Swift?”
This is a natural reaction, given the similarity of their syntax and the relative recency of Swift’s unveiling.
I’ll leave the answer up to you, but Kotlin’s impact is worth considering as you decide which language to invest your time into.
If you’re interested in learning more about Kotlin, we’d love to help you out.
In order to understand Kotlin, it may be useful to see it used to write an Android app.
We won’t touch on the syntax details of each line of code with a direct parallel in Java, but we will take a look at some of the more unique concepts.
Let’s look at KlickerHero, a small clicker game that I’ve written entirely in Kotlin for Android.
Our only activity simply contains a TextView (with ID kounter
) representing the player’s score and a button for the player to click repeatedly (with ID klicker
).
To listen for click events and update the screen accordingly, we’ll need to obtain references to these views.
Let’s take a look at how we do this in KlickerActivity.kt
.
class KlickerActivity : AppCompatActivity() {
private lateinit var kounterTextView : TextView
private lateinit var klickerButton : Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_klicker)
kounterTextView = findViewById(R.id.kounter) as TextView
klickerButton = findViewById(R.id.klicker) as Button
}
}
There are a couple of things to note here.
Let’s first look at kounterTextView
and klickerButton
.
One of the advantages of Kotlin is its null safety.
Every variable must be initialized in Kotlin, helping you avoid the pesky NullPointerException
that plagues the lives of many Java developers.
This isn’t to say that variables can’t ever be null, but a variable that is assigned a null value must broadcast this state to any object that interacts with it.
In our case above, we bend the rule that variables should immediately be initialized by using the lateinit
keyword.
lateinit
is a contract stating that we can declare a variable without initializing it, but we’ll see an UninitializedPropertyAccessException
if we try to use it before initialization.
It’s great for declaring member variables such as views in Android that need to be initialized in onCreate
.
Our KlickerHero game is simple—click the button and your score goes up.
In order to really hook our players in, though, we need career progression.
We’ll track two statistics about our player:
Let’s look at a simple Kotlin data class (with no Android code):
data class Kareer(var kount: Int = 0, var tier: Tier = Tier.Baby) {
fun incrementKount() {
kount++
if (kount > tier.maxKlicks) {
tier = getTierForLevel(tier.level + 1)
}
}
}
Kareer
has two fields, kount
and tier
.
Classes in Kotlin can include constructor declarations directly in their header.
We can even define default arguments in case we aren’t passed a value for kount
or tier
.
Note that we declare kount
and tier
to be var
s as a part of our constructor header.
This gives us member variable without any extra work.
Also note that Kareer
is a data class
.
Specifying a class as a data class in Kotlin gets us a number of method implementations for free, including equals
/hashCode
, toString
, and more.
Kareer
has one function, incrementKount
.
When kount
is incremented, we also want to check to see if the player has levelled up to a new Tier
.
Let’s look at the player’s Tier
, represented by an enumerated type:
enum class Tier(var level : Int = 0, val maxKlicks : Int, @ColorRes val kolorResId : Int) {
Baby(0, 5, R.color.baby_blue),
Novice(1, 15, R.color.light_green),
Pro(2, 30, R.color.light_red),
Master(3, 100, R.color.light_yellow),
Grandmaster(4, Int.MAX_VALUE, R.color.grandmaster_purple);
companion object {
fun getTierForLevel(level: Int) : Tier {
Tier.values()
.asSequence()
.filter { it.level == level }
.forEach { return it }
throw IllegalArgumentException()
}
}
}
Enumerated types in Kotlin look pretty similar to other classes, and they may be familiar to Java developers.
One thing stands out against the rest of the implementation, and that’s the companion object
keyword.
Companion objects are singletons declared within a class, and their usage is often similar to that of statics in Java.
In this case we’ve defined a method getTierForLevel
that will return a Tier
value for a specific level
integer.
getTierForLevel
could iterate through each value in Tier
and return a value matching a specific value, but Kotlin provides functional constructs as a part of the language.
We won’t dig into them as a part of this post, but functional programming can be exceptionally useful when working with data.
Now that we have a data model expressed via the Kareer
data class, let’s use it to display the user’s clicking prowess.
class KlickerActivity : AppCompatActivity() {
...
override fun onCreate(savedInstanceState: Bundle?) {
...
val kareer = Kareer()
klickerButton.setOnClickListener { updateKount(kareer) }
}
private fun updateKount(kareer: Kareer) {
kareer.incrementKount()
val tierColor = ContextCompat.getColor(this, kareer.tier.kolorResId)
kounterTextView.apply {
text = "Count: ${kareer.kount}"
setBackgroundColor(tierColor)
}
}
}
In our KlickerActivity
’s onCreate
method, we’ll instantiate our Kareer
object—no new
keyword is necessary.
We’ll then set a click listener on the klicker
button so that we update the count text and background color of the kounter
TextView
whenever the button is clicked.
The last niceties to introduce here are in the apply
block.
apply
passes whatever instance it is called on into a block in which you can sequentially invoke methods on it.
In this case, we call setText
and setBackgroundColor
on kounterTextView
without repeatedly referencing kounterTextView
.
It’s a sort of tiny scope change, and it’s great for invoking multiple methods on a single instance.
Lastly, note here that we directly assign to text
rather than calling setText
.
Getters and setters are hidden away from you with some syntactic sugar, saving you time spent writing and maintaining these methods.
Google’s support of Kotlin solidifies a movement that was already building growing with significant momentum.
We can’t wait to see more people embrace Kotlin as a part of their everyday workflow in everything from small side projects to production applications at scale.
We’ve only scratched the surface in this post of what Kotlin has to offer.
We’ll be posting more blog content in Kotlin from here on. If you’re interested to learn more about Kotlin Programming, stay updated with our books & bootcamps.
Learn more about what Kotlin means for your Android apps. Download our ebook for a deeper look into how this new first-party language will affect your business.
Introduction When computer programming was young, code was punched into cards. That is, holes were punched into a piece of cardboard in a format...
Jetpack Compose is a declarative framework for building native Android UI recommended by Google. To simplify and accelerate UI development, the framework turns the...
Big Nerd Ranch is chock-full of incredibly talented people. Today, we’re starting a series, Tell Our BNR Story, where folks within our industry share...