Abstract: Kotlin quick reference for Java developer.
Packages
Unlike Java, Kotlin doesn’t impose any restrictions on the layout and naming of source files: you can put multiple classes in the same file, and choose any directory structure you’re comfortable with.
However, it’s still a good practice to follow Java’s directory layout, while not hesitating to group multiple classes into the same file if they’re small and related.
Variables
In Kotlin, there are two keywords to declare a variable:
val
— Immutable reference. Aval
variable can’t be reassigned after initialization and corresponds to afinal
variable in Java.var
— Mutable reference. The value of such variable can be changed.
1 | val i = 42 // final variable |
Semicolons in Kotlin are optional.
Kotlin’s variable declaration starts with one of the two keywords above, followed by the variable name. The variable type may come after the name, separated by a colon. Just like Java, Kotlin is a statically typed language, but the compiler is able to determine the type from the context, this is called type inference:
1 | val hello: String = "Hello" // explicit type |
Kotlin supports top-level variables, you don’t need to declare them in a class.
Equality
Unlike Java, Kotlin’s ==
operator compares two objects by calling equals
under the hood. For reference comparison, you can use the ===
operator instead:
1 | val a = Pair("key", 42) |
For values which are represented as primitive types at runtime, the
===
check is equivalent to the==
check.
Null Safety
The first and most important difference between Kotlin and Java is Kotlin’s explicit support for nullable types. Putting a question mark after the type name explicitly allows the variable to contains null
:
1 | var nickname: String? = null |
A type without a question mark denotes that variables of such type can’t store null
references.
Thankfully, Kotlin provides useful tools to deal with nullable types, and the safe call operator will soon become your best friend:
1 | // returns null if nickname is null |
The safe call operator above pairs extremely well with the Elvis operator:
1 | // return "unknown" if nickname is null |
If you’re missing NPE, you can still throw an exception if the value is null
using the not-null assertion operator:
// throw an NPE if nickname is null
val length = nickname!!.length
String Templates
Kotlin allows you to refer to local variables in string literals:
1 | val name = "Kotlin" |
The literal expression is statically checked and you’re not restricted to variable names, you can also use more complex expressions:
1 | println("Hello ${if (array.size > 0) array[0] else "foo"}!") |
Primitive Types
Unlike Java, Kotlin doesn’t distinguish between primitive and wrapper types. You’ll find below the full list of types that correspond to Java primitives:
- Integer types —
Byte
,Short
,Int
,Long
- Floating-point types —
Float
,Double
- Character type —
Char
- Boolean type —
Boolean
Since there’s no straightforward equivalent to Java primitives, you may be concerned about efficiency, right? Don’t worry, the Kotlin compiler will ensure to use the most efficient type where applicable: Kotlin’s Double
type will be compiled to the Java primitive type double
, unless being used in a Collection
for example or allowed to be null
.
Another important difference between Kotlin and Java, is numeric conversions. Kotlin doesn’t automatically convert numbers from one type to another, the conversion must be explicit:
1 | val i = 42 |
Unsigned integers support is coming to Kotlin.
Arrays
Unlike Java, arrays are just classes in Kotlin. Array instances can be created using the arrayOf
, arrayOfNulls
and emptyArray
standard library functions:
1 | val integers: Array<Int> = arrayOf(1, 2, 3) |
Beware that type arguments of Array
always become object types: an Array<Int>
will compile to java.lang.Integer[]
! To represent an array of primitive types, Kotlin provide separate array classes, one for each primitive type: IntArray
, DoubleArray
, BooleanArray
and so on:
1 | val threeZeros = IntArray(3) |
Enum
Kotlin’s enums are declared by adding the enum
keyword in front of the class
header:
Root Type
Similar to Java’s Object
, the Any
type is the supertype of all non-nullable types in Kotlin. Unlike Java, Any
is also the supertype of primitive types such as Int
:
1 | val i: Any = 42 // automatic boxing |
Under the hood, the
Any
type corresponds tojava.lang.Object
Type Casts
The is
operator checks if an expression is an instance of a type:
1 | fun sizeOf(obj: Any): Int? { |
The as
operator tries to cast a value to a type:
1 | fun foo(obj: Any) { |
The safe cast operator as?
tries to cast a value to the specified type and returns null
if the value doesn’t have the proper type. The safe cast operator pairs extremely well with the Elvis operator:
1 | val stringOrEmpty = obj as? String ?: "" |
Iterating
Kotlin supports both while
and do-while
loops and doesn’t bring anything new to it.
In Kotlin the for
loop construct iterates through anything that provides an iterator but, unlike Java, doesn’t support any other form than the well-known for..in
syntax:
1 | val array = arrayOf("Kotlin", "Java", "Gradle") |
To iterate over numbers, instead, Kotlin supports the concept of range_._ _A_s the name implies, a range is just an interval between two values, usually numbers but not limited to:
1 | // inferred type is IntRange |
_until_
,_downTo_
and_step_
are not Kotlin keywords but infix extension functions from the standard library (more on this later).
The ..
operator, or range operator, when applied to integral types returns an IntRange
object which is a subclass of Iterable<Int>
over which the for
statement iterates. In fact, the 0..100
expression expands to 0.rangeTo(100)
under the hood.
Working with arrays or collections, using the destructuring syntax along with the withIndex
standard library function or the indices
property, you can keep track of the index:
1 | for((index, element) in array.withIndex()) |
Iterating over a map is also easier thanks to the destructuring syntax:
1 | for((key, value) in my_map) |
You can use the in
operator to check whether a value is in a range, or the opposite:
1 | if(c in 'a'..'z') { |
Functions
Kotlin’s functions declaration starts with the fun
keyword, followed by the function name and the parameter list in parentheses. The return type comes after the parameter list, separated by a colon:
1 | // block body |
Kotlin supports top level functions; you can declare functions at the top level of a file, you don’t need to put them in a class.
Unlike Java, Kotlin supports default parameters and named arguments:
1 | fun join(strings: Collection<String>, separator: String = " ", |
Kotlin’s functions can accept an arbitrary number of argument using the vararg
keyword, which pairs extremely well with the spread operator:
1 | fun hello(vararg values: String) { |
Extension Functions
An extension function is a function that can be called as a member of a class, but is defined outside of it:
1 | package strings |
Extension functions do not have access to private or protected members of the receiver class and doesn’t automatically become available across your entire project, it needs to be imported just like any other class or function:
1 | import strings.last |
You must be aware that extension functions behavior is slightly different from member functions:
- Member functions always take precedence if they share the same signature.
- Extension functions resolve to the declared static type of the variable, not to the runtime type of the value:
1 | fun Any.yell() = println("Any!") |
Infix Functions
Functions marked with the [infix](https://kotlinlang.org/docs/reference/functions.html#infix-notation)
keyword can also be called by omitting the dot and the parentheses:
1 | class Price(val value: Double, val currency: Currency) |
Kotlin’s standard library already provides a few infix functions, not to be confused with keywords:
1 | // until and step are infix functions |
infix
functions must be member functions or extension functions, and declare a single parameter with no default value.
Lambdas
In Kotlin, a lambda is always surrounded with curly braces and the arrow separates the argument list from the body. When passed as the last or only argument to a function, more concise constructs are possible:
1 | // Classic |
Unlike Java, you can access non-final variables and even modify them:
1 | var empty = 0 |
Similar to how extension functions works, you can also declare the receiver of a lambda by prefixing its declaration with the type of the receiver:
1 | fun buildString(action: StringBuilder.() -> Unit) : String { |
The buildString
helper above could be implemented using the Kotlin’s standard library [with](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/with.html)
function, which also makes use of lambdas with receiver:
1 | val s = with(StringBuilder()) { |
Inline Functions
Functions declared with the inline
modifier are directly substituted into places where the function is called instead of being invoked:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21inline fun greeter(action: () -> Unit) {
try {
println("Hello!")
action()
}finally{
println("Goodbye!")
}
}
greeter {
println("How are you?")
}
// directly expands to:
try {
println("Hello!")
println("How are you?")
}finally{
println("Goodbye!")
}
As a side effect, a bare return
statement in a lambda called from an inline
function will return from the enclosing function:
1 | // Kotlin's standard library extension |
Instead, you can use returns with labels:
1 | val list = listOf("Kotlin", "", "Java", "Groovy") |
When
The [when](https://kotlinlang.org/docs/reference/control-flow.html#when-expression)
construct may look a lot like the switch
statement, but it is more powerful:
1 | // when is an expression, not a statement! |
Arbitrary expressions can be used as branch conditions:
1 | when(x) { |
If you have complex statement for branch, can just use like that:
1 | val v1:String = "good" |
Exceptions
Use the throw
exception to throw an exception object, but remember there’s no new
keyword in Kotlin:
1 | throw Exception("Oops!") |
The try..catch
block is now an expression:
1 | // the returned value is the last expression of the try or catch block |
In Kotlin, all exceptions are unchecked, meaning that the compiler doesn’t force you to catch any of them:
1 | // Java would requires us to catch IOException here |
Classes
Like Java, classes in Kotlin are declared using the class
keyword, but curly braces are optional if there’s no body. Unlike Java, Kotlin doesn’t have a new
keyword:
1 | class Empty |
Constructors
A class in Kotlin can have a single primary constructor, and one (or more) secondary constructors. The primary constructor goes after the class name:
1 | // Primary constructor declaration |
Secondary constructors are declared in the class body using the constructor
keyword:
1 | class Person(val name: String) { |
Inheritance
Kotlin’s common superclass is Any
, that’s also the default for a class with no supertypes declared. To declare an explicit supertype, place the type after a colon in the class header:
1 | // the base class primary constructor must be called (if any) |
Properties
As you may have noticed above, properties in Kotlin can be declared as mutable using the var
keyword, or immutable using val
, and can also be declared using the primary constructor:
1 | class Point { |
Properties in Kotlin can have getters and setters, also known as custom accessors:
1 | // custom getter on an immutable property (no setter allowed) |
Using the by
keyword, properties can be delegated to an instance through the setValue
and getValue
operator:
1 | class Capitalizer { |
The Kotlin standard library provides the lazy function that takes a lambda and returns an instance of Lazy<T>
and can serve as a delegate:
1 | class DeepThought { |
As a convenience, Kotlin also provides the lateinit
modifier to leave non-null properties un-initialized:
1 | class Presenter { |
Access Modifiers
Unlike Java, Kotlin’s default modifiers are public
and final
, and you’ll have to explicitly mark classes and members with the open
modifier if required. Kotlin also introduced the internal
modifier, which restricts accesses to the current module only, while the private
modifier is now allowed for top-level declarations:
1 | // final public class (default) |
Interfaces
Interfaces in Kotlin can contain abstract properties along with method declarations and implementations:
1 | interface A { |
Thanks to Kotlin’s built-in support for delegation, a derived class implementing a given interface can delegate all (or some) of its public members to a specified object using the by
keyword:
1 | interface View { |
Extension Properties
Similar to extension functions, extension properties provide a way to extend classes using the property syntax instead of the function syntax:
1 | val String.last: Char |
Operator Overloading
Kotlin uses convention methods instead of relying on types to define implementations for a predefined set of operators. To implement an operator, you can provide a member function or an extension function. Functions that overload operators need to be marked as operator
:
1 | class Point(val x: Double, val y: Double) { |
You’ll find below the list of the predefined operators, excluding delegated properties operators for brevity:
╔═══════════════════╦═════════════════════════════════╗
║ Expression ║ Translate to ║
╠═══════════════════╬═════════════════════════════════╣
║ a + b ║ a.plus(b) ║
║ a - b ║ a.minus(b) ║
║ a b ║ a.times(b) ║
║ a / b ║ a.div(b) ║
║ a % b ║ a.rem(b) ║
║ +a ║ a.unaryPlus() ║
║ -a ║ a.unaryMinus() ║
║ !a ║ a.not() ║
║ a++ ║ a.inc() ║
║ a– ║ a.dec() ║
║ a..b ║ a.rangeTo(b) ║
║ a in b ║ b.contains(a) ║
║ a !in b ║ !b.contains(a) ║
║ a[i] ║ a.get(i) ║
║ a[i, j] ║ a.get(i, j) ║
║ a[i, …, z] ║ a.get(i, …, z) ║
║ a[i] = b ║ a.set(i, b) ║
║ a[i, j] = b ║ a.set(i, j, b) ║
║ a[i, …, z] = b ║ a.set(i, …, z, b) ║
║ a() ║ a.invoke() ║
║ a(i) ║ a.invoke(i) ║
║ a(i, j) ║ a.invoke(i, j) ║
║ a(i, …, z) ║ a.invoke(i, …, z) ║
║ a+=b ║ a.plusAssign(b) ║
║ a-=b ║ a.minusAssign(b) ║
║ a=b ║ a.timesAssign(b) ║
║ a/=b ║ a.divAssign(b) ║
║ a%=b ║ a.remAssign(b) ║
║ a == b ║ a?.equals(b) ?: (b === null) ║
║ a != b ║ !(a?.equals(b) ?: (b === null)) ║
║ a > b ║ a.compareTo(b) > 0 ║
║ a < b ║ a.compareTo(b) < 0 ║
║ a >= b ║ a.compareTo(b) >= 0 ║
║ a <= b ║ a.compareTo(b) <= 0 ║
║ for(a in b) ║ b.iterator() ║
║ val (x, y, z) = a ║ val x = a.component1() ║
║ ║ val y = a.component2() ║
║ ║ val z = a.component3() ║
╚═══════════════════╩═════════════════════════════════╝
Both
inc
anddec
operators must return a value and shouldn’t mutate the object.
Data Classes
Kotlin provides autogenerated equals
, hashCode
, toString
and copy
methods implementations for free by adding the data
modifier to your class:
1 | data class Person(val name: String, val age: Int) |
Nested Classes
Unlike Java, inner classes in Kotlin don’t have access to the outer class instance, unless explicitly requested by adding the inner
modifier. Kotlin’s default behavior is equivalent to a static
nested class in Java, but an outer class doesn’t see private
members of its inner (or nested) classes:
1 | class A { |
Sealed Classes
Sealed classes are used to represent restricted class hierarchy: a sealed
class can have subclasses, but all of them must be declared in the same file as the sealed
class itself:
1 | sealed class Expr |
A sealed class is abstract by itself, it cannot be instantiated directly and can have abstract members.
Classes which extend subclasses of a sealed class (indirect inheritors) can be placed anywhere, not necessarily in the same file.
The object keyword
The object
keyword defines a class and creates an instance of that class at the same time. The singleton pattern comes immediately in mind as a use case:
1 | // object class as a singleton |
Anonymous objects replace Java’s anonymous inner classes but, unlike Java, they can implement multiple interfaces:
1 | val listener = object: OnMenuClickListener, OnMenuExpandListener { |
Classes in Kotlin can’t have static
members since there’s no such keyword in the Kotlin language. Instead, top-level and object declarations are recommended but if you need to access private members of an outer class, companion objects are the way to go:
1 | class Person private constructor(val name: String, val age: Int) { |
Conclusion
There are still quite a few unexplored corners like coroutines, generics or java interoperability but, if you come that far, you should be ready to switch to Kotlin.
Do not forget to dive into the Koans exercises to practice what you learned, while keeping a hand at the official documentation. You can also write and execute Kotlin’s snippets online!
That guide will be updated over time and suggestions, so do not hesitate to follow me? Thanks for reading!