A Day with Kotlin
What Kotlin has that Java does not.
So, I was hearing a lot about Kotlin lately and decided to give it a try. It will be fun learning about a new language and maybe I find it useful for some product or app.
Kotlin’s official documentation is great and there are many introductory articles to Kotlin language on medium, so I won’t be explaining the basics and syntax but wanted to present some unique features that Java doesn’t have and makes Kotlin stand out.
1. Null Safety
One of the most common pitfalls of many programming languages including Java, is that accessing a null reference would result in a null reference exception. In Java, this is known as NullPointerException
or NPE.
Kotlin’s type system eliminates the danger of null references from code. Kotlin would throw NullPointerException
only if:
- programmer calls
throw NullPointerException();
explicitly - usage of the
!!
operator - some data inconsistency with regard to initialization
- Java interpolation
Kotlin distinguishes between null
(nullable references) and non null
(non-null references) types. Types are non-null by default and can be made nullable by adding a ?
var a: String = "abc" // non-null reference by default
a = null // compile time error!
// now if you call a method on a, its guaranteed not to cause a NPE
var len = a.length // fine
var b: String? = "abc" // nullable reference
b = null // okay
// but if you access same property on b
// that won't be safe and compiler throws an error!
var len = b.length // error: b can be null
// you can use safe call operator ?. though
var len = b?.length // okay, this will return the length
// if b is not null and will return null otherwise
2. Smart Casts
Kotlin tracks your logic and auto-casts types if possible. No more instanceof
checks followed by explicit casts as in Java.
var s = "abc"
if (s is String)
println(s.length)
Kotlin keeps track of is
checks and you don’t have to use explicit cast operators.
fun demo(x: Any) {
if (x is String)
println(x.length) // x is automatically cast to String
}
3. String Templates
Strings may contain template expressions i.e. code that is evaluated and its result is concatenated into the string.
A template expression starts with a $
sign and can be a simple name like this:
val n = 10
println("n = $n") // prints n = 10
or can be an arbitrary expression in braces like this:
val s = "abc"
println("$s.length is ${s.length}") // prints abc.length is 3
you can evaluate expressions like this:
val a = 3
val b = 5
println("Sum of $a and $b is ${a + b}")
// prints Sum of 3 and 5 is 8
4. Properties
Classes in Kotlin can have properties. No need to bloat your code with explicitly written getters and setters as in Java. Properties can be declared mutable using var
keyword or read-only using val
keyword.
class Address {
var name: String = ""
var street: String = ""
var city: String = ""
var state: String? = ""
var zip: String = ""
}
Usage:
fun copyAddress(address: Address): Address {
val result = Address() // there is no new keyword in Kotlin!
result.name = address.name // accessors are called
result.street = address.street
// ...
return result
}
5. Primary Constructors
A class declaration in Kotlin consists of:
- class name
- class header (primary constructor and other type parameters)
- class body
A class in Kotlin can have a primary constructor like so:
class Person(firstName: String) {
}
and you can use initalizer blocks as:
class Customer(name: String) {
val customerKey = name.toUpperCase()
}
There are secondary constructors also, check out.
6. Type Inference
Kotlin infers data types of variables and properties. So, you can omit types wherever you feel it will improve readability.
val a = "abc" // type inferred to String
val b = 4 // type inferred to Int
val c: Double = 0.7 // type declared explicitly
val d: List<String> = ArrayList() // type declared explicitly
Explicit conversions are also supported. Smaller types are NOT implicitly converted to bigger types. This means we cannot assign a value of type Byte
to an Int
variable without an explicit conversion as follows:
val b: Byte = 1 // fine, literals are checked statically
val i: Int = b // Error!
// use explicit conversions to widen numbers
val i: Int = b.toInt() // Okay, explicitly widened
Kotlin infers types from the context and arithmetical operations are overloaded for appropriate conversions. e.g.
val num = 1L + 3 // Long + Int => Long
7. Ranges
Range expressions in Kotlin are written with ..
, in
and !in
operators.
for (i in 1..10) // i ∈ [1, 10] both inclusive
println(i)
If you want to iterate in reverse order. use downTo()
function as so:
for (i in 4 downTo 1)
print(i) // prints 4321
Iterating over numbers in arbitrary step instead of default 1, use step()
function as so:
for (i in 1..4 step 2)
print(i) // prints 13
for (i in 4 downTo 1 step 2)
print(i) // prints 42
Want to exclude last element? use until()
function.
for (i in 1 until 10) // i ∈ [0, 10) 10 is excluded.
print(i) // prints 123456789
See other important function here. That’s why I like to say that Kotlin is Python of the Java world!
8. Extension Functions
Kotlin provides you the ability to extend an existing class with new functionality without having to inherit from the class or use any kind of design pattern such as Decorator. This is done via special declarations called extensions. Kotlin provides extension functions and extension properties.
Example of adding a swap
functionality to MutableList<Int>
as:
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val temp = this[index1] // 'this' corresponds to the list
this[index1] = this[index2]
this[index2] = temp
}
// now you can call such a function on any MutableList<Int> object
val numbers = mutableListOf(1, 2, 3)
numbers.swap(0, 2)
println(numbers) // prints [3, 2, 1]
9. Operator Overloading
Java doesn’t support operator overloading. Kotlin does. When you use an operator in Kotlin, its corresponding member function is called. e.g. expression a + b
transforms to a.plus(b)
under the hood. plus
function is overloaded to work with various Kotlin basic types and String
.
You can overload operators for your own defined types. e.g.
class Point(val x: Int = 0, val y: Int = 10) {
// overloading plus operator
operator fun plus(p: Point): Point {
return Point(x + p.x, y + p.y)
}
}
// now you can add 2 Point's as
val p1 = Point(3, -8)
val p2 = Point(2, 9)
val sum = Point()
sum = p1 + p2
println("sum = (${sum.x}, ${sum.y})") // prints sum = (5, 1)
Here the plus function is marked with operator
to tell the compiler that +
operator is being overloaded here. Expressionp1 + p2
is transformed into p1.plus(p2)
under the hood.
10. Destructuring Declarations
Its convenient to destructure an object into a number of variables. e.g.
val (name, age) = person
This is called destructuring declarations. A destructuring declaration is compiled down into following code:
val name = person.component1()
val age = person.component2()
Destructuring declarations also work in for-loops as:
for ((a, b) in collection) { ... }
Probably the nicest way to traverse a map is
for ((key, value) in map) {
// do something with key and value
}
Destructuring declarations also helps in returning 2 values from a function. e.g.
data class Result(val result: Int, val status: Status)
fun function(...): Result {
// other code
return Result(result, status)
}
Other than above mentioned major features, Kotlin also supports ==
(structural equality) and ===
(referential equality), default arguments, named arguments, when
expression as a better switch
case statement, lambdas and higher order functions.
Conclusion
Overall, I think Kotlin is a great language, safe by default and very pleasant to work with. I have just shaved the tip of the iceberg here, there is a lot more to cover. If you are a Scala developer, you will probably not find many new things in Kotlin. But if you are a Java developer and are tired of the boilerplate and all the ceremonies before you get to do any real work, then Kotlin is for you.
You can also check out my medium blog here. Cheers!