Kotlin for Java fans — Basics

Mohammad Fayaz
6 min readNov 6, 2023

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

  1. 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:

  1. lazy is a higher-order function that takes a lambda as its argument and returns an instance of Lazy<T>. The lambda contains the initialization code for the property.
  2. by lazy is a delegate syntax in Kotlin. It means that the property lazyProperty doesn't hold the actual value but instead delegates the responsibility of getting the value to the Lazy instance returned by the lazy function.
  3. 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 the lazyProperty.

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

Take diversion, not allowed here by Tamilazhagan, link

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.

--

--