Professional Documents
Culture Documents
Any programming language is only as good as its tool support. Kotlin has
outstanding support for modern IDEs such as Android Studio, IntelliJ Idea, and
Eclipse. Common tasks like code assistance or refactoring are handled properly.
The Kotlin team works hard to make the Kotlin plugin better with every single
release. Most of the bugs are quickly fixed and many of the features requested
by the community are implemented.
Kotlin aims to bring you the best of both worlds by combining concepts and
elements from both procedural and functional programming. It follows many
guidelines are described in the book, Effective Java, 2nd Edition, by Joshua
Bloch which is considered must read a book for every Java developer.
On top of that, Kotlin is open sourced, so we can check out the project and be
actively involved in any aspect of the Kotlin project such as Kotlin plugins,
compilers, documentations or Kotlin language itself.
Awesome Kotlin examples
Kotlin is really easy to learn for Android developers because the syntax is
similar to Java and Kotlin often feels like natural Java evolution. At the
beginning, a developer usually writes Kotlin code having in mind habits from
Java, but after a while, it is very easy to move to more idiomatic Kotlin
solutions. Let's look at some cool Kotlin features, and see where Kotlin may
provide benefits by solving common programming tasks in an easier, more
concise, and more flexible way. We have tried to keep examples simple and self-
explanatory, but they utilize content from various parts of this book, so it's fine if
they are not fully understood at this point. The goal of this section is to focus on
the possibilities and present what can be achieved by using Kotlin. This section
does not necessarily need to fully describe how to achieve it. Let's start with
a variable declaration:
var name = "Igor" // Inferred type is String
name = "Marcin"
Notice that Kotlin does not require semicolons. You can still use them, but they
are optional. We also don't need to specify a variable type because it's inferred
from the context. Each time the compiler can figure out the type
from the context we don't have to explicitly specify it. Kotlin is a strongly typed
language, so each variable has an adequate type:
var name = "Igor"
name = 2 // Error, because name type is String
The variable has an inferred String type, so assigning a different value (integer)
will result in compilation error. Now, let's see how Kotlin improves the way to
add multiple strings using string templates:
val name = "Marcin"
println("My name is $name") // Prints: My name is Marcin
We need no more joining strings using the + character. In Kotlin, we can easily
incorporate single variable or even whole expression into string literals:
val name = "Igor"
println("My name is ${name.toUpperCase()}")
// Prints: My name is IGOR
In Java any variable can store null values. In Kotlin strict null safety forces us to
explicitly mark each variable that can store nullable values:
var a: String = "abc"
a = null // compilation error
Adding a question mark to a data type (string versus string?), we say that
variable can be nullable (can store null references). If we don't mark variable as
nullable, we will not be able to assign a nullable reference to it. Kotlin also
allows to deal with nullable variables in proper ways. We can use safe call
operator to safely call methods on potentially nullable variables:
savedInstanceState?.doSomething
Kotlin also has several new data types. Let's look at the Range data type that
allows us to define end inclusive ranges:
for (i in 1..10) {
print(i)
} // 12345678910
Kotlin introduces the Pair data type that, combined with infix notation, allows us
to hold a common pair of values:
val capitol = "England" to "London"
println(capitol.first) // Prints: England
println(capitol.second) // Prints: London
Immutable collection means that the collection state can't change after
initialization (we can't add/remove items). Mutable collection (quite obviously)
means that the state can change.
With lambda expressions, we can use the Android framework build in a very
concise way:
view.setOnClickListener {
println("Click")
}
Kotlin standard library (stdlib) contains many functions that allow us to perform
operations on collections in simple and concise way. We can easily perform
stream processing on lists:
val text = capitols.map { (country, _) -> country.toUpperCase() }
.onEach { println(it) }
.filter { it.startsWith("P") }
.joinToString (prefix = "Countries prefix P:")
// Prints: ENGLAND POLAND
println(text) // Prints: Countries prefix P: POLAND
.joinToString (prefix = "Countries prefix P:")
Notice that we don't have to pass parameters to a lambda. We can also define our
own lambdas that will allow us to write code in completely new way. This
lambda will allow us to run a particular piece of code only in Android
Marshmallow or newer.
inline fun supportsMarshmallow(code: () -> Unit) {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
code()
}
//usage
supportsMarshmallow {
println("This code will only run on Android Nougat and newer")
}
We can make asynchronous requests easily and display responses on the main
thread using the doAsync function:
doAsync {
var result = runLongTask() // runs on background thread
uiThread {
toast(result) // run on main thread
}
}
The Kotlin compiler knows that the variable x is of the type String after
performing a check, so it will automatically cast it to the String type, allowing it
to call all methods and access all properties of the String class without any
explicit casts.
Using default argument syntax, we can define the default value for each function
argument and call it in various ways:
fun printMessage(product: String, amount: Int = 0,
name: String = "Anonymous") {
println("$name has $amount $product")
}
printMessage("oranges") // Prints: Anonymous has 0 oranges
printMessage("oranges", 10) // Prints: Anonymous has 10 oranges
printMessage("oranges", 10, "Johny")
// Prints: Johny has 10 oranges
The only limitation is that we need to supply all arguments without default
values. We can also use named argument syntax to specify function arguments:
printMessage("oranges", name = "Bill")
This also increases readability when invoking the function with multiple
parameters in the function call.
The data classes give a very easy way to define and operate on classes from the
data model. To define a proper data class, we will use the data modifier before
the class name:
data class Ball(var size:Int, val color:String)
val ball = Ball(12, "Red")
println(ball) // Prints: Ball(size=12, color=Red)
Notice that we have a really nice, human readable string representation of the
class instance and we do not need the new keyword to instantiate the class. We
can also easily create a custom copy of the class:
val ball = Ball(12, "Red")
println(ball) // prints: Ball(size=12, color=Red)
val smallBall = ball.copy(size = 3)
println(smallBall) // prints: Ball(size=3, color=Red)
smallBall.size++
println(smallBall) // prints: Ball(size=4, color=Red)
println(ball) // prints: Ball(size=12, color=Red)
The preceding constructs make working with immutable objects very easy and
convenient.
One of the best features in Kotlin are extensions. They allow us to add new
behavior (a method or property) to an existing class without changing its
implementation. Sometimes when you work with a library or framework, you
would like to have extra method or property for certain class. Extensions are a
great way to add those missing members. Extensions reduce code verbosity and
remove the need to use utility functions known from Java (for example, the
StringUtils class). We can easily define extensions for custom classes, third-party
libraries, or even Android framework classes. First of all, ImageView does not have
the ability to load images from network, so we can add the loadImage extension
method to load images using the Picasso library (an image loading library for
Android):
fun ImageView.loadUrl(url: String) {
Picasso.with(context).load(url).into(this)
}
\\usage
imageView.loadUrl("www.test.com\\image1.png")
We can also add a simple method displaying toasts to the Activity class:
fun Context.toast(text:String) {
Toast.makeText(this, text, Toast.LENGTH_SHORT).show()
}
//usage (inside Activity class)
toast("Hello")
There are many places where usage of extensions will make our code simpler
and more concise. Using Kotlin, we can fully take advantage of lambdas to
simplify Kotlin code even more.
Interfaces in Kotlin can have default implementations as long as they don't hold
any state:
interface BasicData {
val email:String
val name:String
get() = email.substringBefore("@")
}
All the presented examples are only a glimpse of what can be accomplished with
Kotlin. We will learn how to utilize the power of Kotlin throughout this book.