Kotlin for Java fans — Basics
What is Kotlin? 🤔
Kotlin’s official website says
A modern programming language that makes developers happier. Developed by JetBrains & Open-source Contributors
Kotlin is a cross-platform, statically typed, general-purpose programming language with type inference, designed to interoperate fully with Java, and the JVM version of Kotlin’s standard library depends on the Java Class Library, but type inference allows its syntax to be more concise. Let’s understand more about Kotlin by comparing it with Java.
Kotlin is Concise
at the same time for newcomers the syntaxes can be frustrating and might require time to understand. However Kotlin really avoids a lot of boilerplate the implementation of it is quite complex and you don’t have to worry about it unless you are trying to build Kotlin libraries or work on existing libraries.
Hello World
We know we must need a class to write a main function in Java, it’s not the case with Kotlin. Let’s see a snippet of code in both languages.
Hello World in Java
public class Main {
public static void main(String[] args) {
System.out.println("Hello World");
}
}
Hello World in Kotlin
fun main() {
println("Hello World")
}
In Kotlin we don’t need a class, no need to mark it static, public, arguments are also optional, no semi-colons, no System.out. That’s a lot of boilerplate.
Types
Kotlin doesn’t force you to write types for the variable, instead it uses type inheriting strategy and declaring the type is an optional thing here. In Kotlin, everything is an object in the sense that you can call member functions and properties on any variable. It also supports extension functions which are very handy and improve developer experience.
Kotlin supports all the basic data types supported by Java.
- Integers
- Byte
- Short
- Int
- Long - Floating Points
- Float
- Double - Characters
- Strings
- Booleans
// Integer types
val oneByte: Byte = 1
val smallValue: Short = 44
val one = 1 // Int
val two: Int = 1 // Int
val threeBillion = 3000000000 // Long
val twoLakh = 2_00_000 // Long
val oneLong = 1L // Long
// Floating points
val pi: Float = 3.14f
val latitude: Double = 72.23849
// Character
val a = 'a'
// String
val name = "John Doe"
val isEarthFlat = false
Kotlin does not have primitive types in the same way Java does. Instead, Kotlin treats basic types as objects. For example, Int
, Long
, Float
, Double
, Boolean
, and Char
are all objects in Kotlin. So no confusion at all whether we have to choose int
or Integer
in Kotlin, it’s all Int
Variables
There are two types of variables in Kotlin
1. Mutable — can change over time
2. Immutable — don’t change over time
var mutable = 13
mutable = 23
val immutable = 44
// cannot do this
immutable = 33
Constants
Constants in Kotlin can only be declared in object and companion object. Even in top-level of a class you can’t declare constants.
// acceptable
const val PI = 3.14
// not acceptable
fun main() {
const val PI_2 = 3.14
}
class ConstantsClass {
// not acceptable
const val CODE = "38"
// acceptable
companion object {
const val NEW_CODE = 23
}
}
object Constants {
const val CODE = "38"
}
Lateinit variables, Null Safety
Are you one of those who is obsessed with the following error
Exception in thread “main” java.lang.NullPointerException
Kotlin has got you covered for this, it has sound null-safety and has solution for most of your problems, consider the following scenarios
- Let’s have a look at
lateinit
, you need a globally scoped variable in a class and you want to initialise that later.
class College {
val name = "ABC College"
lateinit var principal: String
fun assignPrincipal(name: String) {
principal = name
}
fun getPrincipalName(): String {
return if (::principal.isInitialized) {
principal
} else {
"Unassigned"
}
}
}
Here, the variable principal
is not initialized during declaration, instead we are assigning it some value in future point of time. In the other method getPrincipalName
we can check if the variable is initialized using the runtime reference symbol ::
which gives us access to few other properties such as
- isAbstract
- isConst
- isFinal
- isOpen (for classes)
- isLateinit
- isSuspend (for methods)
I don’t feel like giving an explanation for the above properties, they’re pretty much self-explanatory.
2. Time for securing variables from being null.
fun main() {
println(College().getPrincipalName())
val college = College()
college.assignPrincipal("Test")
println(college.getPrincipalName())
}
class College {
val name = "ABC College"
var principal: String? = null
fun assignPrincipal(name: String) {
principal = name
}
fun getPrincipalName(): String {
principal?.let {
return it
}
return "Unassigned"
}
}
/**
Output:
Unassigned
Test
**/
Here we use the ?
operator while declaring a variable, if we know that this can be null and whenever we want to access it, we use ?.let
and access the value safely.
What if you are sure, that the type which was declared as nullable, is definitely not null, here is how you access the variable.
fun getPrincipalName(): String {
return principal!!
}
You must be careful with this though, as this can potentially throw a NullPointerException, if the value is null
The Lazy Opearator
Let’s start with basic snippet
val lazyProperty: Type by lazy {
// Initialization code
// This code block will be executed only on the first access
// The last expression in this block will be the value assigned to lazyProperty
}
Here’s how it works:
lazy
is a higher-order function that takes a lambda as its argument and returns an instance ofLazy<T>
. The lambda contains the initialization code for the property.by lazy
is a delegate syntax in Kotlin. It means that the propertylazyProperty
doesn't hold the actual value but instead delegates the responsibility of getting the value to theLazy
instance returned by thelazy
function.- The lambda is the code that computes the value of the property. This code is executed only on the first access of the
lazyProperty
property. The result of the lambda expression is the value of thelazyProperty
.
Full example:
class Example {
val expensiveComputation: String by lazy {
println("Computing the value...")
// Expensive computation here
"Computed Value"
}
}
fun main() {
val example = Example()
println("Before accessing property")
// The expensiveComputation property is not computed yet
println(example.expensiveComputation)
println("After accessing property")
// The expensiveComputation property is computed only on the first access
println(example.expensiveComputation)
}
Output:
Before accessing property
Computing the value...
Computed Value
After accessing property
Computed Value
Functions in Kotlin
fun sayHello(param1: String, param2: Int): Unit {
println("Hello")
}
Kotlin has a different function syntax, every function starts with a keyword fun
unlike return type in Java, the return type appears after params declared and before function body.
You can return any type similar to Java, any primitive object or custom classes. There is also support for lambda functions, look at the example below.
fun getSize() = 15
From the above code, you can see we skipped adding return type, there’s automatic inheritance of type.
We also have inline
, noinline
and crossinline
functions. Checkout existing blog for a brief, https://www.baeldung.com/kotlin/crossinline-vs-noinline
Suspend functions
Suspend functions, found in languages like Kotlin and frameworks like Android, allow developers to write asynchronous code that resembles synchronous code. By pausing and resuming execution, these functions simplify complex operations, making the codebase more readable and maintainable.
Simplifying Concurrency
Suspend functions eradicate the infamous “callback hell” by enabling linear, sequential code for asynchronous tasks. This simplifies the management of concurrent operations, enhancing code clarity and developer productivity.
Efficient Error Handling
These functions facilitate unified error handling, reducing the complexity associated with managing exceptional cases. Unlike traditional methods, suspend functions offer a more concise and efficient way to handle errors in asynchronous operations.
import kotlinx.coroutines.*
// Define a suspend function to fetch data from an API
suspend fun fetchDataFromAPI(): String {
delay(2000) // Simulate a network delay of 2 seconds
return "Data from API"
}
fun main() {
// Using coroutine scope to launch a coroutine
runBlocking {
println("Fetching data from API...")
// Launch a coroutine to fetch data asynchronously
val result = async {
fetchDataFromAPI()
}
// Do some other work while waiting for the API response
println("Doing other work...")
// Wait for the API response and print the result
println("API Result: ${result.await()}")
}
}
Output:
Fetching data from API...
Doing other work...
API Result: Data from API
Will continue with more concepts in another blog. Thanks for reading. Follow for more such content.