Procedural programming style Kotlin relaxes the Java restriction on
static methods and variables, allowing them to exist outside a class body. Static objects and functions can be defined at the top level of the package without requiring a redundant class-level scope. For compatibility with Java, Kotlin provides the JvmName annotation, which specifies the class name used when the package is viewed from a Java project. For example, @file:JvmName("JavaClassName").
Main entry point As in
C,
C++,
C#,
Java, and
Go, the
entry point to a Kotlin
program is a function named main, which may be passed an array containing any
command-line arguments. This is optional since Kotlin 1.3.
Perl,
PHP, and
Unix shell–style
string interpolation is supported.
Type inference is also supported. // Hello, World! example fun main() { val scope = "World" println("Hello, $scope!") } fun main(args: Array) { for (arg in args) { println(arg) } }
Extension functions Similar to C#, Kotlin allows adding an
extension function to any class without the formalities of creating a derived class with new functions. An extension function has access to all the public interface of a class, which it can use to create a new function interface to a target class. An extension function will appear exactly like a class function and will be shown in code completion inspections of class functions. For example: package org.wikipedia.examples fun String.lastChar(): Char = get(length - 1) println("Kotlin".lastChar()) // prints: n By placing the preceding code in the top-level of a package, the String class is extended to include a function that was not included in the original definition of the String class. // Overloading '+' operator using an extension function operator fun Point.plus(other: Point): Point { return Point(x + other.x, y + other.y) } val p1 = Point(10, 20) val p2 = Point(30, 40) println(p1 + p2) // prints: Point(x=40, y=60)
Scope functions Kotlin has five scope functions that allow changing the
scope within the context of an
object. The scope functions are , , , , and .
Unpack arguments with the spread operator Similar to Python, the spread operator asterisk (*) unpacks an array's contents as individual arguments to a function, e.g.: fun main(args: Array) { val list: List = listOf("args: ", *args) println(list) }
Destructuring declarations Destructuring declarations decompose an object into multiple variables at once, e.g., a 2D coordinate object might be
destructured into two integers, and . For example, the object supports destructuring to simplify access to its key and value fields: for ((key, value) in map) { println("$key: $value") }
Nested functions Kotlin allows local functions to be declared inside other functions or methods. class User(val id: Int, val name: String, val address: String) fun saveUserToDb(user: User) { fun validate(user: User, value: String, fieldName: String) { require(value.isNotEmpty()) { "Can't save user ${user.id}: empty $fieldName" } } validate(user, user.name, "Name") validate(user, user.address, "Address") // Save user to the database // ... }
Classes are final by default In Kotlin, to derive a new class from a base class, the base class must be explicitly marked as open; in contrast, most object-oriented languages, such as Java, are open by default. Example of a base class that is open to deriving a new subclass from it: // open on the class means this class will allow derived classes open class MegaButton { // no-open on a function means that // polymorphic behavior disabled if function overridden in derived class fun disable() { // ... } // open on a function means that // polymorphic behavior allowed if function is overridden in derived class open fun animate() { // ... } } class GigaButton: MegaButton() { // Explicit use of override keyword required to override a function in derived class override fun animate() { println("Giga Click!") } }
Abstract classes are open by default Abstract classes define abstract, or "pure virtual", placeholder functions that will be implemented in a derived class. Abstract classes are open by default. // No need for the open keyword here, it's already open by default abstract class Animated { // This virtual function is already open by default as well abstract fun animate() open fun stopAnimating() { // ... } fun animateTwice() { // ... } }
Classes are public by default Kotlin provides the following keywords to restrict visibility for top-level declarations (such as classes) and for class members: public, internal, protected, and private. When applied to a class member: When applied to a top-level declaration: Example: // Class is visible only to current module internal open class TalkativeButton { // method is only visible to current class private fun yell() = println("Hey!") // method is visible to current class and derived classes protected fun whisper() = println("Let's talk!") } internal class MyTalkativeButton: TalkativeButton() { fun utter() = super.whisper() } MyTalkativeButton().utter()
Primary constructor vs. secondary constructors Kotlin supports specifying a "primary constructor" as part of the class definition itself, consisting of an argument list following the class name. This argument list supports an expanded syntax on Kotlin's standard function argument lists that enables declaration of class properties in the primary constructor, including visibility, extensibility, and mutability attributes. Additionally, when defining a subclass, properties in super-interfaces and super-classes can be overridden in the primary constructor. // Example of class using primary constructor syntax // (Only one constructor required for this class) open class BaseUser(open var isSubscribed: Boolean) open class PowerUser(protected val nickname: String, final override var isSubscribed: Boolean = true):BaseUser(isSubscribed) { } However, when more than one constructor is needed for a class, a more general constructor can be defined using
secondary constructor syntax, which closely resembles that of most object-oriented languages such as C++, C#, and Java. // Example of class using secondary constructor syntax // (more than one constructor required for this class) class Context class AttributeSet open class View(ctx:Context) { constructor(ctx: Context, attr: AttributeSet): this(ctx) } class MyButton : View { // Constructor #1 constructor(ctx: Context) : super(ctx) { // ... } // Constructor #2 constructor(ctx: Context, attr: AttributeSet) : super(ctx, attr) { // ... } }
Sealed classes Sealed classes and interfaces restrict subclass hierarchies, meaning more control over the inheritance hierarchy. Declaration of sealed interface and class: sealed interface Expr sealed class Job All the subclasses of the sealed class are defined at compile time. No new subclasses can be added to it after the module containing the sealed class is compiled. For example, a sealed class in a compiled jar file cannot be subclassed. sealed class Vehicle data class Car(val brandName: String, val owner: String, val color: String): Vehicle() class Bike(val brandName: String, val owner: String, val color: String): Vehicle() class Tractor(val brandName: String, val owner: String, val color: String): Vehicle() val kiaCar = Car("KIA", "John", "Blue") val hyundaiCar = Car("Hyundai", "Britto", "Green")
Data classes Kotlin's data class construct defines classes whose primary purpose is storing data, similar to Java's record types. Like Java's record types, the construct is similar to a regular class, except that the key methods equals, hashCode, and toString are automatically generated from the class's properties. Unlike Java's records, data classes are open for inheritance.
Kotlin interactive shell $ kotlinc-jvm type :help for help; :quit for quit >>> 2 + 2 4 >>> println("Hello, World!") Hello, World!
Kotlin as a scripting language Kotlin can also be used as a
scripting language. A script is a Kotlin source file using the
filename extension, with executable source code at the top-level scope: // list_folders.kts import java.io.File val folders = File(args[0]).listFiles { file -> file.isDirectory() } folders?.forEach(::println) Scripts can be run by passing the -script option and the corresponding script file to the compiler. $ kotlinc -script list_folders.kts "path_to_folder_to_inspect"
Null safety Kotlin distinguishes between
nullable and non-nullable data types. All nullable objects must be declared with a "?" postfix after the type name. Operations on nullable objects need special care from developers: a null-check must be performed before using the value, either explicitly, or with the aid of Kotlin's null-safe operators: • (the
safe navigation operator) can be used to safely access a method or property of a possibly null object. If the object is null, the method will not be called, and the expression evaluates to null. Example: • (the
null coalescing operator) is a binary operator that returns the first operand, if non-null, else the second operand. It is often referred to as the
Elvis operator, due to its resemblance to an
emoticon representation of Elvis Presley. {{blockquote | fun sayHello(maybe: String?, neverNull: Int) { // use of Elvis operator val name: String = maybe ?: "stranger" println("Hello $name") } }}
Lambdas Kotlin supports
higher-order functions and
anonymous functions, or
lambdas. // the following function takes a lambda, f, and executes f passing it the string "lambda" // note that (String) -> Unit indicates a lambda with a String parameter and Unit return type fun executeLambda(f: (String) -> Unit) { f("lambda") } Lambdas are declared using braces, {{mono|{ }}}. If a lambda takes parameters, they are declared within the braces and followed by the operator. // the following statement defines a lambda that takes a single parameter and passes it to the println function val l = { c : Any? -> println(c) } // lambdas with no parameters may simply be defined using { } val l2 = { print("no parameters") }
"Hello world" example (Taken from and explained at https://kotlinlang.org/docs/kotlin-tour-hello-world.html.) fun main() { println("Hello, world!") // Hello, world! } ==Tools==