mirror of
https://github.com/adambard/learnxinyminutes-docs.git
synced 2024-12-24 01:51:38 +00:00
25a08b5931
Refactored the operator overloading example, fixed some grammar mistakes, reordered parts of the code to make it easier to read and some other minor fixes.
444 lines
14 KiB
Kotlin
444 lines
14 KiB
Kotlin
---
|
|
language: kotlin
|
|
contributors:
|
|
- ["S Webber", "https://github.com/s-webber"]
|
|
filename: LearnKotlin.kt
|
|
---
|
|
|
|
Kotlin is a statically typed programming language for the JVM, Android and the
|
|
browser. It is 100% interoperable with Java.
|
|
[Read more here.](https://kotlinlang.org/)
|
|
|
|
```kotlin
|
|
// Single-line comments start with //
|
|
/*
|
|
Multi-line comments look like this.
|
|
*/
|
|
|
|
// The "package" keyword works in the same way as in Java.
|
|
package com.learnxinyminutes.kotlin
|
|
|
|
/*
|
|
The entry point to a Kotlin program is a function named "main".
|
|
The function is passed an array containing any command-line arguments.
|
|
Since Kotlin 1.3 the "main" function can also be defined without
|
|
any parameters.
|
|
*/
|
|
fun main(args: Array<String>) {
|
|
/*
|
|
Declaring values is done using either "var" or "val".
|
|
"val" declarations cannot be reassigned, whereas "vars" can.
|
|
*/
|
|
val fooVal = 10 // we cannot later reassign fooVal to something else
|
|
var fooVar = 10
|
|
fooVar = 20 // fooVar can be reassigned
|
|
|
|
/*
|
|
In most cases, Kotlin can determine what the type of a variable is,
|
|
so we don't have to explicitly specify it every time.
|
|
We can explicitly declare the type of a variable like so:
|
|
*/
|
|
val foo: Int = 7
|
|
|
|
/*
|
|
Strings can be represented in a similar way as in Java.
|
|
Escaping is done with a backslash.
|
|
*/
|
|
val fooString = "My String Is Here!"
|
|
val barString = "Printing on a new line?\nNo Problem!"
|
|
val bazString = "Do you want to add a tab?\tNo Problem!"
|
|
println(fooString)
|
|
println(barString)
|
|
println(bazString)
|
|
|
|
/*
|
|
A raw string is delimited by a triple quote (""").
|
|
Raw strings can contain newlines and any other characters.
|
|
*/
|
|
val fooRawString = """
|
|
fun helloWorld(val name : String) {
|
|
println("Hello, world!")
|
|
}
|
|
"""
|
|
println(fooRawString)
|
|
|
|
/*
|
|
Strings can contain template expressions.
|
|
A template expression starts with a dollar sign ($).
|
|
*/
|
|
val fooTemplateString = "$fooString has ${fooString.length} characters"
|
|
println(fooTemplateString) // => My String Is Here! has 18 characters
|
|
|
|
/*
|
|
For a variable to hold null it must be explicitly specified as nullable.
|
|
A variable can be specified as nullable by appending a ? to its type.
|
|
We can access a nullable variable by using the ?. operator.
|
|
We can use the ?: operator to specify an alternative value to use
|
|
if a variable is null.
|
|
*/
|
|
var fooNullable: String? = "abc"
|
|
println(fooNullable?.length) // => 3
|
|
println(fooNullable?.length ?: -1) // => 3
|
|
fooNullable = null
|
|
println(fooNullable?.length) // => null
|
|
println(fooNullable?.length ?: -1) // => -1
|
|
|
|
/*
|
|
Functions can be declared using the "fun" keyword.
|
|
Function arguments are specified in brackets after the function name.
|
|
Function arguments can optionally have a default value.
|
|
The function return type, if required, is specified after the arguments.
|
|
*/
|
|
fun hello(name: String = "world"): String {
|
|
return "Hello, $name!"
|
|
}
|
|
println(hello("foo")) // => Hello, foo!
|
|
println(hello(name = "bar")) // => Hello, bar!
|
|
println(hello()) // => Hello, world!
|
|
|
|
/*
|
|
A function parameter may be marked with the "vararg" keyword
|
|
to allow a variable number of arguments to be passed to the function.
|
|
*/
|
|
fun varargExample(vararg names: Int) {
|
|
println("Argument has ${names.size} elements")
|
|
}
|
|
varargExample() // => Argument has 0 elements
|
|
varargExample(1) // => Argument has 1 elements
|
|
varargExample(1, 2, 3) // => Argument has 3 elements
|
|
|
|
/*
|
|
When a function consists of a single expression then the curly brackets can
|
|
be omitted. The body is specified after the = symbol.
|
|
*/
|
|
fun odd(x: Int): Boolean = x % 2 == 1
|
|
println(odd(6)) // => false
|
|
println(odd(7)) // => true
|
|
|
|
// If the return type can be inferred then we don't need to specify it.
|
|
fun even(x: Int) = x % 2 == 0
|
|
println(even(6)) // => true
|
|
println(even(7)) // => false
|
|
|
|
// Functions can take functions as arguments and return functions.
|
|
fun not(f: (Int) -> Boolean): (Int) -> Boolean {
|
|
return {n -> !f.invoke(n)}
|
|
}
|
|
// Named functions can be specified as arguments using the :: operator.
|
|
val notOdd = not(::odd)
|
|
val notEven = not(::even)
|
|
// Lambda expressions can be specified as arguments.
|
|
val notZero = not {n -> n == 0}
|
|
/*
|
|
If a lambda has only one parameter
|
|
then its declaration can be omitted (along with the ->).
|
|
The name of the single parameter will be "it".
|
|
*/
|
|
val notPositive = not {it > 0}
|
|
for (i in 0..4) {
|
|
println("${notOdd(i)} ${notEven(i)} ${notZero(i)} ${notPositive(i)}")
|
|
}
|
|
|
|
// The "class" keyword is used to declare classes.
|
|
class ExampleClass(val x: Int) {
|
|
fun memberFunction(y: Int): Int {
|
|
return x + y
|
|
}
|
|
|
|
infix fun infixMemberFunction(y: Int): Int {
|
|
return x * y
|
|
}
|
|
}
|
|
/*
|
|
To create a new instance we call the constructor.
|
|
Note that Kotlin does not have a "new" keyword.
|
|
*/
|
|
val fooExampleClass = ExampleClass(7)
|
|
// Member functions can be called using dot notation.
|
|
println(fooExampleClass.memberFunction(4)) // => 11
|
|
/*
|
|
If a function has been marked with the "infix" keyword then it can be
|
|
called using infix notation.
|
|
*/
|
|
println(fooExampleClass infixMemberFunction 4) // => 28
|
|
|
|
/*
|
|
Data classes are a concise way to create classes that just hold data.
|
|
The "hashCode"/"equals" and "toString" methods are automatically generated.
|
|
*/
|
|
data class DataClassExample (val x: Int, val y: Int, val z: Int)
|
|
val fooData = DataClassExample(1, 2, 4)
|
|
println(fooData) // => DataClassExample(x=1, y=2, z=4)
|
|
|
|
// Data classes have a "copy" function.
|
|
val fooCopy = fooData.copy(y = 100)
|
|
println(fooCopy) // => DataClassExample(x=1, y=100, z=4)
|
|
|
|
// Objects can be destructured into multiple variables.
|
|
val (a, b, c) = fooCopy
|
|
println("$a $b $c") // => 1 100 4
|
|
|
|
// destructuring in "for" loop
|
|
for ((a, b, c) in listOf(fooData)) {
|
|
println("$a $b $c") // => 1 100 4
|
|
}
|
|
|
|
val mapData = mapOf("a" to 1, "b" to 2)
|
|
// Map.Entry is destructurable as well
|
|
for ((key, value) in mapData) {
|
|
println("$key -> $value")
|
|
}
|
|
|
|
// The "with" function is similar to the JavaScript "with" statement.
|
|
data class MutableDataClassExample (var x: Int, var y: Int, var z: Int)
|
|
val fooMutableData = MutableDataClassExample(7, 4, 9)
|
|
with (fooMutableData) {
|
|
x -= 2
|
|
y += 2
|
|
z--
|
|
}
|
|
println(fooMutableData) // => MutableDataClassExample(x=5, y=6, z=8)
|
|
|
|
/*
|
|
We can create a list using the "listOf" function.
|
|
The list will be immutable - elements cannot be added or removed.
|
|
*/
|
|
val fooList = listOf("a", "b", "c")
|
|
println(fooList.size) // => 3
|
|
println(fooList.first()) // => a
|
|
println(fooList.last()) // => c
|
|
// Elements of a list can be accessed by their index.
|
|
println(fooList[1]) // => b
|
|
|
|
// A mutable list can be created using the "mutableListOf" function.
|
|
val fooMutableList = mutableListOf("a", "b", "c")
|
|
fooMutableList.add("d")
|
|
println(fooMutableList.last()) // => d
|
|
println(fooMutableList.size) // => 4
|
|
|
|
// We can create a set using the "setOf" function.
|
|
val fooSet = setOf("a", "b", "c")
|
|
println(fooSet.contains("a")) // => true
|
|
println(fooSet.contains("z")) // => false
|
|
|
|
// We can create a map using the "mapOf" function.
|
|
val fooMap = mapOf("a" to 8, "b" to 7, "c" to 9)
|
|
// Map values can be accessed by their key.
|
|
println(fooMap["a"]) // => 8
|
|
|
|
/*
|
|
Sequences represent lazily-evaluated collections.
|
|
We can create a sequence using the "generateSequence" function.
|
|
*/
|
|
val fooSequence = generateSequence(1, { it + 1 })
|
|
val x = fooSequence.take(10).toList()
|
|
println(x) // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
|
|
|
// An example of using a sequence to generate Fibonacci numbers:
|
|
fun fibonacciSequence(): Sequence<Long> {
|
|
var a = 0L
|
|
var b = 1L
|
|
|
|
fun next(): Long {
|
|
val result = a + b
|
|
a = b
|
|
b = result
|
|
return a
|
|
}
|
|
|
|
return generateSequence(::next)
|
|
}
|
|
val y = fibonacciSequence().take(10).toList()
|
|
println(y) // => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
|
|
|
|
// Kotlin provides higher-order functions for working with collections.
|
|
val z = (1..9).map {it * 3}
|
|
.filter {it < 20}
|
|
.groupBy {it % 2 == 0}
|
|
.mapKeys {if (it.key) "even" else "odd"}
|
|
println(z) // => {odd=[3, 9, 15], even=[6, 12, 18]}
|
|
|
|
// A "for" loop can be used with anything that provides an iterator.
|
|
for (c in "hello") {
|
|
println(c)
|
|
}
|
|
|
|
// "while" loops work in the same way as other languages.
|
|
var ctr = 0
|
|
while (ctr < 5) {
|
|
println(ctr)
|
|
ctr++
|
|
}
|
|
do {
|
|
println(ctr)
|
|
ctr++
|
|
} while (ctr < 10)
|
|
|
|
/*
|
|
"if" can be used as an expression that returns a value.
|
|
For this reason the ternary ?: operator is not needed in Kotlin.
|
|
*/
|
|
val num = 5
|
|
val message = if (num % 2 == 0) "even" else "odd"
|
|
println("$num is $message") // => 5 is odd
|
|
|
|
// "when" can be used as an alternative to "if-else if" chains.
|
|
val i = 10
|
|
when {
|
|
i < 7 -> println("first block")
|
|
fooString.startsWith("hello") -> println("second block")
|
|
else -> println("else block")
|
|
}
|
|
|
|
// "when" can be used with an argument.
|
|
when (i) {
|
|
0, 21 -> println("0 or 21")
|
|
in 1..20 -> println("in the range 1 to 20")
|
|
else -> println("none of the above")
|
|
}
|
|
|
|
// "when" can be used as a function that returns a value.
|
|
var result = when (i) {
|
|
0, 21 -> "0 or 21"
|
|
in 1..20 -> "in the range 1 to 20"
|
|
else -> "none of the above"
|
|
}
|
|
println(result)
|
|
|
|
/*
|
|
We can check if an object is of a particular type by using the "is" operator.
|
|
If an object passes a type check then it can be used as that type without
|
|
explicitly casting it.
|
|
*/
|
|
fun smartCastExample(x: Any) : Boolean {
|
|
if (x is Boolean) {
|
|
// x is automatically cast to Boolean
|
|
return x
|
|
} else if (x is Int) {
|
|
// x is automatically cast to Int
|
|
return x > 0
|
|
} else if (x is String) {
|
|
// x is automatically cast to String
|
|
return x.isNotEmpty()
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
println(smartCastExample("Hello, world!")) // => true
|
|
println(smartCastExample("")) // => false
|
|
println(smartCastExample(5)) // => true
|
|
println(smartCastExample(0)) // => false
|
|
println(smartCastExample(true)) // => true
|
|
|
|
// Smartcast also works with when block
|
|
fun smartCastWhenExample(x: Any) = when (x) {
|
|
is Boolean -> x
|
|
is Int -> x > 0
|
|
is String -> x.isNotEmpty()
|
|
else -> false
|
|
}
|
|
|
|
/*
|
|
Extensions are a way to add new functionality to a class.
|
|
This is similar to C# extension methods.
|
|
*/
|
|
fun String.remove(c: Char): String {
|
|
return this.filter {it != c}
|
|
}
|
|
println("Hello, world!".remove('l')) // => Heo, word!
|
|
}
|
|
|
|
// Enum classes are similar to Java enum types.
|
|
enum class EnumExample {
|
|
A, B, C
|
|
}
|
|
|
|
fun printEnum() = println(EnumExample.A) // => A
|
|
|
|
/*
|
|
The "object" keyword can be used to create singleton objects.
|
|
We cannot instantiate it but we can refer to its unique instance by its name.
|
|
This is similar to Scala singleton objects.
|
|
*/
|
|
object ObjectExample {
|
|
fun hello(): String {
|
|
return "hello"
|
|
}
|
|
|
|
override fun toString(): String {
|
|
return "Hello, it's me, ${ObjectExample::class.simpleName}"
|
|
}
|
|
}
|
|
|
|
|
|
fun useSingletonObject() {
|
|
println(ObjectExample.hello()) // => hello
|
|
// In Kotlin, "Any" is the root of the class hierarchy, just like "Object" is in Java
|
|
val someRef: Any = ObjectExample
|
|
println(someRef) // => Hello, it's me, ObjectExample
|
|
}
|
|
|
|
|
|
/* The not-null assertion operator (!!) converts any value to a non-null type and
|
|
throws an exception if the value is null.
|
|
*/
|
|
var b: String? = "abc"
|
|
val l = b!!.length
|
|
|
|
data class Counter(var value: Int) {
|
|
// overload Counter += Int
|
|
operator fun plusAssign(increment: Int) {
|
|
this.value += increment
|
|
}
|
|
|
|
// overload Counter++ and ++Counter
|
|
operator fun inc() = Counter(value + 1)
|
|
|
|
// overload Counter + Counter
|
|
operator fun plus(other: Counter) = Counter(this.value + other.value)
|
|
|
|
// overload Counter * Counter
|
|
operator fun times(other: Counter) = Counter(this.value * other.value)
|
|
|
|
// overload Counter * Int
|
|
operator fun times(value: Int) = Counter(this.value * value)
|
|
|
|
// overload Counter in Counter
|
|
operator fun contains(other: Counter) = other.value == this.value
|
|
|
|
// overload Counter[Int] = Int
|
|
operator fun set(index: Int, value: Int) {
|
|
this.value = index + value
|
|
}
|
|
|
|
// overload Counter instance invocation
|
|
operator fun invoke() = println("The value of the counter is $value")
|
|
|
|
}
|
|
/* You can also overload operators through an extension methods */
|
|
// overload -Counter
|
|
operator fun Counter.unaryMinus() = Counter(-this.value)
|
|
|
|
fun operatorOverloadingDemo() {
|
|
var counter1 = Counter(0)
|
|
var counter2 = Counter(5)
|
|
counter1 += 7
|
|
println(counter1) // => Counter(value=7)
|
|
println(counter1 + counter2) // => Counter(value=12)
|
|
println(counter1 * counter2) // => Counter(value=35)
|
|
println(counter2 * 2) // => Counter(value=10)
|
|
println(counter1 in Counter(5)) // => false
|
|
println(counter1 in Counter(7)) // => true
|
|
counter1[26] = 10
|
|
println(counter1) // => Counter(value=36)
|
|
counter1() // => The value of the counter is 36
|
|
println(-counter2) // => Counter(value=-5)
|
|
}
|
|
```
|
|
|
|
### Further Reading
|
|
|
|
* [Kotlin tutorials](https://kotlinlang.org/docs/tutorials/)
|
|
* [Try Kotlin in your browser](https://play.kotlinlang.org/)
|
|
* [A list of Kotlin resources](http://kotlin.link/)
|