The best way to understand a language is to write code in it. Not read about it, not watch webinars, not save articles to "read later" (we all know how that ends).
Open an IDE and build something that actually works.
That's exactly what this tutorial is for. We'll go from zero to a first working mini-app. If you know Java, things will move even faster — I'll be drawing parallels the whole way.
Running Kotlin: two options, one of which takes zero minutes
Right in the browser. Head to play.kotlinlang.org and just start writing. Nothing to install, nothing to configure, no Stack Overflow rabbit holes about why the setup failed. Perfect for a first look.
In IntelliJ IDEA. Kotlin support is built right in. Create a new Kotlin project and you're ready to go. No plugins, no ceremony.
Your first program:
fun main() {
println("Hello, Kotlin!")
}In Java, that same thing looks like this:
public class Main {
public static void main(String[] args) {
System.out.println("Hello, Kotlin!");
}
}Both do exactly the same thing. Kotlin just doesn't have public static void or the wrapper class, because... why did we need those again? Good question. Java never quite answered it.
val and var: two words to remember
In Kotlin, variables are declared with either val or var.
val name = "Alice" // Immutable. Like final in Java.
var age = 25 // Mutable.
age = 26 // Fine.
name = "Bob" // Compile error.
Simple rule: use val everywhere you don't need to change the value. var only when you actually need mutation. It's like making a promise upfront: "I'm not going to change this." And Kotlin makes sure you keep it.
You don't need to specify the type — Kotlin infers it:
val score = 100 // Kotlin figures out: this is an Int
val name = "Alice" // This is a StringOr explicitly, if you prefer:
val score: Int = 100
val name: String = "Alice"Null-safety: how Kotlin kills NullPointerException
Tony Hoare invented null in 1965 and later called it his "billion-dollar mistake." Sounds like self-criticism, but honestly — it's underselling it. NullPointerException has cost the industry far more than a billion dollars.
In Java, any variable can turn out to be null. The compiler stays quiet. You find out at runtime. In production. Sometimes on a live demo in front of a client — yes, that happens too.
Kotlin solves this at the type system level:
var name: String = "Alice"
name = null // Compile error. Right away. Before anything runs.If a variable can be null, you have to say so explicitly using ?:
var name: String? = "Alice"
name = null // We agreed it's nullable — now that's fine.So what do you do when you need to call a method on something that might be null?
println(name?.length) // If null — returns null. Doesn't crash.
println(name?.length ?: 0) // If null — returns 0. Elvis operator.It's called the Elvis operator — because ?: looks like Elvis Presley's hair. That's not a joke. It's the actual explanation in the official documentation.
Functions
In Java:
public static int add(int a, int b) {
return a + b;
}In Kotlin:
fun add(a: Int, b: Int): Int {
return a + b
}Or even shorter — for single-expression functions:
fun add(a: Int, b: Int) = a + bKotlin supports default parameter values. In Java, you'd write several overloaded methods for this. In Kotlin — just one:
fun greet(name: String, greeting: String = "Hello") {
println("$greeting, $name!")
}
greet("Alice") // Hello, Alice!
greet("Bob", "Hey") // Hey, Bob!String templates — $name right inside the string. Kotlin substitutes the value directly. No +, no concatenation — like a sensible language should work.
Data class: goodbye, boilerplate
Here's a classic task. You need a model: a user with a name, age, and email.
In Java that means a constructor, three getters, three setters, equals(), hashCode(), toString(). Or Lombok. Or accepting your fate and writing it all by hand like it's 2005.
In Kotlin:
data class User(val name: String, val age: Int, val email: String)One line. The compiler generates everything else, including copy():
val alice = User("Alice", 30, "alice@example.com")
val olderAlice = alice.copy(age = 31)
println(alice)
// User(name=Alice, age=30, email=alice@example.com)Look at copy(). You create a new object, changing only the fields you need. In Java you'd usually write a Builder for this. Or just suffer.
Extension functions: adding methods to classes you don't own
Imagine you want to add a method to the String class. In Java — you write a utility class with static methods, and then everywhere you're writing StringUtils.doSomething(myString). It works, but it feels like a workaround.
In Kotlin — extension functions:
fun String.isValidEmail(): Boolean {
return this.contains("@") && this.contains(".")
}
println("user@example.com".isValidEmail()) // true
println("not-an-email".isValidEmail()) // falseIt reads like a native method on the class. No inheritance, no wrappers. You just added a method — and now you use it.
Putting it all together: a user validator
Let's take everything we've covered and write something real.
data class User(val name: String, val email: String?)
fun String.isValidEmail() = contains("@") && contains(".")
fun validateUser(user: User): String {
val emailStatus = user.email?.let {
if (it.isValidEmail()) "valid" else "invalid"
} ?: "not provided"
return "${user.name}: email $emailStatus"
}
fun main() {
val users = listOf(
User("Alice", "alice@example.com"),
User("Bob", "bob-not-email"),
User("Charlie", null)
)
users.forEach { println(validateUser(it)) }
}
Output:
Alice: email valid
Bob: email invalid
Charlie: email not providedNotice: null is handled without a single if. The ?. and ?: operators take care of it elegantly. Charlie didn't provide an email — and there's no NPE. Life is good.
This was just the beginning
We covered the basics here: variables, null-safety, functions, data classes, extension functions.
It gets more interesting from here. Coroutines — async code without the pain. Sealed classes — a powerful alternative to switch that won't let you forget to handle a case. Functional collection operations — map, filter, reduce in one line.
If you want to learn Kotlin systematically rather than in scattered pieces from browser tabs — give our course a try. 62 levels, 1,100+ tasks with auto-verification, 3 projects for your portfolio. The first level is free.
Keep reading
- What is Kotlin and Why Developers Are Adding It to Their Resumes — if you want the bigger picture: where the language came from, why Google bet on it, and what the Oracle story is all about.
- Java vs Kotlin: An Honest Comparison — now that you've seen both syntaxes, take a look at a detailed breakdown: null-safety, data classes, coroutines, interoperability.
GO TO FULL VERSION