I stumbled upon Kotlin and had so much fun with it that I wanted to share it with you, so I'm writing a series called Kotlin from Zero to web dev, which will cover the basics of web development with Kotlin. My development environment is IntelliJ and macOS, so this will be written accordingly.
Hello Kotlin
We will create an example that prints Hello World to the console. I created the project as shown below.

If you look at the Main.kt
file, you'll see that it looks like this
fun main(args: Array<String>) {
println("Hello World!")
// Try adding program arguments via Run/Debug configuration.
// Learn more about running applications: https://www.jetbrains.com/help/idea/running-applications.html.
println("Program arguments: ${args.joinToString()}")
}
- Line 1: The declaration of the
main
function, which is the program's entry point. - Line 2: Code to print the string Hello World to the console.
- Lines 4-5: Comments. Explained below.
- Line 6: Code to print the program arguments.run Main.kt
$ ... MainKt arg1 arg2 arg3
outputHello World! Program arguments: arg1 arg2 arg3
💡 All subsequent example code is written inside the main
method, and will omit to reduce duplication.
Comment
- One-line comments : Use the
//
symbol to write a single-line comment. Anything after//
is treated as a comment and is valid only up to the end of the line. Single-line comments are often used to explain code, temporarily disable code, or write developer notes. - Multi-line comments : You can write comments that span multiple lines using multi-line comments that start with
/*
and end with*/
. Multi-line comments are often used to write long comment blocks, descriptions of functions or classes, license information, etc. - KDoc comments : These are comments that start with
/**
and end with*/
and are used to create API documentation for your code, which can be extracted by a tool or IDE to generate API documentation.
Declaring variables
There are two ways to declare variables in Kotlin: var
and val
.
var
: A mutable variable. You can change the value at any time.var age = 30 age = 31
val
: immutable variable. Once assigned a value, it cannot be changed.However, you should be careful with variables declared asval age = 30 age = 31 // ❌ Error
val
because only references are immutable and objects are mutable.val sb = StringBuilder("hello") sb.append("Object") sb.append("is") sb.append("mutable")
Basic types of variable
In Cotlin, variables do not distinguish between primitive and wrapper types.
In other words, there is no distinction between int
(primitive type) and Integer
(wrapper type) like in Java, just one type, Int
.
You might think that treating all variables as objects might be performance inefficient.
This is actually not the case, as Cotlin is automatically optimized when compiled to bytecode, converting to primitive types where they are needed and wrapper types where they are needed.
Now let's see what types of data types we have.
Integer types
Type | Size (bits) | Min value | Max value |
---|---|---|---|
Byte | 8 | -128 | 8 |
Short | 16 | -32768 | 16 |
Int | 24 | -2,147,483,648 (-231) | 2,147,483,647 (231 - 1) |
Long | 32 | -9,223,372,036,854,775,808 (-263) | 9,223,372,036,854,775,807 (263 - 1) |
- If no type is explicitly specified, the type is automatically inferred.
- If it does not exceed the range
Int
, it is anInt
; if it does, it is aLong
type.
- If it does not exceed the range
- To explicitly specify a
Long
type, add the suffix L.
val one = 1 // Int
val threeBillion = 3000000000 // Long
val oneLong = 1L // Long
val oneByte: Byte = 1
Floating-point types

Type | Size (bits) | Significant bits | Exponent bits | Decimal digits |
---|---|---|---|---|
Float | 32 | 24 | 8 | 6-7 |
Double | 64 | 53 | 11 | 15-16 |
- If the type is not explicitly specified, it is inferred to be a
Double
type. - To explicitly specify a
Float
type, add the suffix f.- If the number contains more than 6 or 7 decimal places, it is rounded up.
val e = 2.7182818284 // Double val eFloat = 2.7182818284f // Float, actual value is 2.7182817
- Unlike other languages, there is no implicit scaling for numbers in Kotlin.
For example, a function with a parameter type of
Double
can only be called for aDouble
value, and cannot be called passing aFloat
,Int
, or any other numeric value.fun printDouble(d: Double) { print(d) } val i = 1 val d = 1.0 val f = 1.0f printDouble(d) printDouble(i) // ❌ Error: Type mismatch printDouble(f) // ❌ Error: Type mismatch
Boolean
The Boolean
type is a data type that can have either true or false values.
Built-in operations for Boolean
include the following:
||
- Disjunction(Logical OR)&&
- Conjunction(Logical AND)!
- Negation(Logical NOT)
val myTrue: Boolean = true
val myFalse: Boolean = false
val boolNull: Boolean? = null
println(myTrue || myFalse) // true
println(myTrue && myFalse) // false
println(!myTrue) // false
Characters
Kotlin manages characters in UTF-16 BE (Big Endian), occupying 16 bits of memory space.
Characters are represented by the Char
type, and character literals are enclosed in single quotes.
💡 Literal refers to a value that has meaning in and of itself when expressed in code. For example, an integer literal like 42 represents the number 42 directly in code and has the meaning of the number 42 without any additional processing or operations. In other words, it is a code representation of a number, character, true-false, array, etc.
To represent special characters, Kotlin supports escape sequences, which are composed of a backslash (\
) followed by specific character combinations.
These escape sequences can also be used within strings. Here are some common escape sequences:
\t
: tab\b
: backspace\n
: new line (LF)\r
: carriage return (CR) - Moves the cursor to the beginning of the current line.\'
: single quotation mark\"
: double quotation mark\\
: backslash\$
: dollar sign
You can also encode different characters using Unicode escape sequence syntax,
starting with \u
followed by a combination of four hexadecimal digits.
val aChar = '\u0061'
println(aChar) // a
println(aChar.code) // 97
Strings
The String
type is a data type that represents a string and is represented as a sequence of characters enclosed in double quotes, as shown below.
val str = "a1b2"
String elements can be iterated over in a for
loop.
for (c in str) {
println(c)
}
a
1
b
2
Kotlin's strings inherit the immutability characteristic of Java strings. This means that once a string is initialized, its value cannot be changed or reassigned. Additionally, all operations that transform strings return a new string while leaving the original string unchanged.
var str = "abcd 123"
println(str.uppercase()) // ABCD 123
println(str.replace("abcd","efgh")) // efgh 123
println(str) // abcd 123
It uses the +
operator to concatenate strings, and as long as the first element is a string, it will work even if it is concatenated with values of other types.
val s = "abc" + 1
println(s + "def")
Let's also talk about string literals. There are two types of string literals available in Cotlin.
Escape strings
As we mentioned in the character part, escape characters can also be used in strings.
val s = "Hello, world!\n"
Multiline strings
Multiline strings can contain newlines and arbitrary text. They are used via triple double quotes ("""
) and are output exactly as they are typed, so you can't use escape characters.
fun main(args: Array<String>) {
val text = """
for (c in "foo")
print(c)
"""
println(text)
}
for (c in "foo")
print(c)
To remove leading whitespace from a multiline string, use trimMargin()
.
fun main(args: Array<String>) {
val text = """
|Tell me and I forget.
|Teach me and I remember.
|Involve me and I learn.
|(Benjamin Franklin)
""".trimMargin()
println(text)
}
Tell me and I forget.
Teach me and I remember.
Involve me and I learn.
(Benjamin Franklin)
By default, the pipe character |
is used as the margin prefix, but you can specify a specific character by passing it as a parameter, such as trimMargin(">")
.
String literals can contain template expressions.
A template expression is a piece of code that is executed and the result is concatenated into a string.
They start with a dollar sign ($
), followed by the name of the variable.
val i = 10
println("i = $i") // i = 10
Alternatively, you can put expression in curly braces.
val s = "abc"
println("$s.length is ${s.length}") // abc.length is 3
String templates can be used in both multi-line strings and escaped strings, allowing you to use escape characters within multiline strings as well.
// Prints \$_9.99 ❌
val priceIncorrect = """
\$_9.99
"""
// Prints $_9.99 ⭕️
val price = """
${'$'}_9.99
"""
- In a multiline string, the dollar sign represents a template and should be escaped.
- However, you cannot use the backslash as an escape character.
- You can use the dollar sign in an expression by enclosing it in curly braces.
Arrays
In Cotlin, arrays are represented by the Array
class.
This class has the get()
and set()
functions, which are internally converted to indexing operators ([]
), and other useful member functions.
To create an array, use arrayOf()
to pass values to the function, such as arrayOf(1,2,3)
, which creates the array [1,2,3]
.
If you want to create an empty array, you can use arrayOfNulls()
, which will create an array of the specified size filled with nulls
.
Another option for creating an array is to use a function that takes a size in the array
constructor and an index as an argument.
// Creates an Array<String> with values ["0", "1", "4", "9", "16"]
val asc = Array(5) { i -> (i * i).toString() }
asc.forEach { println(it) }
In Cotlin, arrays are invariant, which simply means that they are treated as completely independent types without considering the inheritance relationships between different types.
This means that you can't assign an Array<String>
to an Array<Any>
in Cotlin, avoiding possible runtime errors.
For now, we'll leave it at that, and leave covariant, semi-covariant, and uncovariant for another article.
💡 Any is the top-level type, and all types implicitly inherit from it.
As mentioned earlier, Kotlin does not distinguish between primitive types and wrapper types.
Therefore, when using large arrays, there can be boxing overhead.
To mitigate this, Kotlin provides primitive type arrays like ByteArray
, ShortArray
, IntArray
, and so on.
While these classes are not inherited from Array
, they have the same methods, and there are factory functions available for each of them.
val x: IntArray = intArrayOf(1, 2, 3)
x[0] = x[1] + x[2]
// Array of int of size 5 with values [0, 0, 0, 0, 0]
val arr = IntArray(5)
// Example of initializing the values in the array with a constant
// Array of int of size 5 with values [42, 42, 42, 42, 42]
val arr = IntArray(5) { 42 }
// Example of initializing the values in the array using a lambda
// Array of int of size 5 with values [0, 1, 2, 3, 4] (values initialized to their index value)
var arr = IntArray(5) { it * 1 }
💡 Boxing overhead is the performance penalty caused by wrapping primitive data type values in objects.
Type casting functions
The primitive data types provide type casting functions to support casting between types.
These conversion functions are named with a to
and follow the name of the target data type, and the list includes:
toByte()
: Converts another data type to a byte.toShort()
: Converts another data type to a Short type.toInt()
: Converts another data type to an Int type.toLong()
: Converts another data type to a Long type.toFloat()
: Converts another data type to a Float type.toDouble()
: Converts another data type to a Double type.toChar()
: Converts another data type to a Char type.
These type conversion functions are used when you can safely convert values without losing data. However, it's important to note that if the value being converted cannot be perfectly mapped to the target data type, an exception may be thrown. Therefore, it's essential to verify that a safe cast is possible.