AI
fun main() { println("Hello, world!") }
fun main() = println("Hello, world!")
fun main(args: Array<String>) { // Run > Edit Configurations → Program arguments println("Hello") println(args.joinToString(", ")) }
fun main(args: Array<String>) { println("Hello"); // <-- Semicolon not needed in Kotlin! println(args.joinToString(", ")) }
fun main(args: Array<String>) { print("Hello") println(", world!") }
print
println
val/var myValue: Type = someValue
var
val
Type
val a: Int = 1 // immediate assignment val newA = 1 // 'Int' type is inferred newA = 4 // Error: Val cannot be reassigned var b = 2 // 'Int' type is inferred b = a // Reassigning to 'var' is okay b = "Some other type" // Error: Inferred type was String but Int was expected val c: Int // Type required when no initializer is provided c = 3 // Deferred assignment a = 4 // Error: Val cannot be reassigned
const val myValue: Type = someValue
const val
MYVALUE
const val NAME = "Kotlin" // can be calculated at compile-time val nameLowered = NAME.lowercase() // cannot be calculated at compile-time
object declaration
companion object
String
primitive type
fun sum(a: Int, b: Int): Int { return a + b }
fun
name
name: type
read-only
end
fun sum(a: Int, b: Int): Int = a + b
When the function body consists of a single expression the curly braces can be omitted and body can be specified after = symbol
single expression
curly braces
=
fun sum(a: Int, b: Int): Int = a + b sum(1, 2) // <-- Function invocation fun main(args: Array<String>) { println(sum(1, 2)) }
fun sum(a: Int, b: Int): Int = a + b sum(a = 1, b = 2) // invocation with named arguments sum(b = 2, a = 1) // order of arguments can be changed sum(1, b = 2) // mixing positional and named arguments
fun sum(a: Int = 0, b: Int = 0): Int = a + b sum(1) // passes 1 for `a`, uses default value for `b`, returns 1 sum(b = 2) // passes 2 for `b`, uses default value for `a`, returns 2 sum() // uses default value for both `a` and `b`, returns 0
Unit
fun myPrint(text: String): Unit = println(text)
fun myPrint(text: String) = println(text)
fun mul(a: Int, b: Int): Int { return a * b } fun mul(a: Int, b: Int) = a * b // single expression function reduced fun printMul(a: Int, b: Int): Unit { println(mul(a, b)) } fun printMul(a: Int = 1, b: Int) { // Unit can be omitted println(mul(a, b)) } fun printMul(a: Int, b: Int = 1) = println(mul(a, b))
if
// block body variant fun max(a: Int, b: Int): Int { if (a > b) { return a } else { return b } }
fun max(a: Int, b: Int): Int { return if (a > b) a else b }
// expression body variant fun max(a: Int, b: Int): Int = if (a > b) { a } else { b }
fun max(a: Int, b: Int) = if (a > b) a else b // same as ternary operator ?:
fun max(a: Int, b: Int) = if (a > b) { println("first branch") a // `a` is result of if statement block } else { println("second branch") b // `b` is result of if statement else block }
If if statement body is not a single expression the last expression in the if statement body evaluates as its return value
last expression
when
when (x) { 1 -> print("x == 1") 2 -> print("x == 2") else -> { print("x is neither 1 nor 2") } }
when returns the same way if does
when { x < 0 -> print("x < 0") x > 0 -> print("x > 0") else -> { print("x == 0") } }
condition can be inside of branches
inside
enum class Role { PROFESSOR, ASSISTANT, STUDENT } val role = Role.PROFESSOR val result = when (role) { Role.PROFESSOR, Role.ASSISTANT -> "Access Granted" Role.STUDENT -> error("Student can’t modify the course.") }
when can accept several options in one branch
else branch can be omitted if when block is used as a statement
else
when block
Kotlin supports following boolean operators:
||
&&
!
There are also extension function counter parts that can be used:
or
and
not
if (a && b) { ... }
VS
if (a and b) { ... }
&& - performs short-circuit evaluation and - infix function, evaluates both arguments
if (a || b) { ... }
if (a or b) { ... }
or - both arguments are evaluated as well
while
val items = listOf("apple", "banana", "kiwi") var index = 0 while (index < items.size) { println("item at $index is ${items[index]}") index++ } var toComplete: Boolean do { ... toComplete = ... } while(toComplete)
The condition variable can be initialized inside of the do…while loop.
do…while
val intRange: IntRange = 1..10 // sequence from 1 to 10 val charRange: CharRange = 'a'..'z' // sequence from `a` to `z` val longRange: LongRange = 1L..10L
Range
..
closed
inclusive
Int
Char
Long
for (x in 1..5) { print(x) }
1..5
1 to 5
for (x in 1 until 5) { print(x) }
1 until 5
1 to 4
for (x in 9 downTo 0) { print(x) }
Java
for (int x=9; x>=0; x--) { System.out.print(x); }
9 downTo 0
downTo
T.rangeTo(that:T)
for (x in 9 downTo 0 step 3) { print(x) }
for (int x=9; x>=0; x=x-3) { System.out.print(x); }
step is an extension function that defines the step of the progression
step
for
Iterating over collections
val items = listOf("apple", "banana", "kiwi") for (item in items) { println(item) }
in
item
Iterating over collections with index
val items = listOf("apple", "banana", "kiwi") for ((index, item) in items.withIndex()) { println("item at $index is $item") }
withIndex() function returns a sequence of IndexedValuewhich contains both index and value
withIndex()
IndexedValue
compile time
runtime problem
are part
Nullable
val notNullText: String = “Not Null String”
val notNullText: String = null // Error: Null can not be a value
null can’t be assigned to a variable of not nullable String type
null
val possiblyNullText: String? = null // All good
? after type indicates that the variable can hold null value
?
val possiblyNullText2: String? = ”Can be a null or not null”
@Nullable
@NotNull
val nullableText: String? = null nullableText.length() // Error: Compile time error - unsafe access
if (nullableText != null) { nullableText.length() // Safe to access, smart casting }
smartcast works only for a local variables or a global read-only variables.
smartcast
nullableText?.length() // Safe to access
?.
someThing?.otherThing // does not throw an NPE if someThing is null
fun printDepartmentHead(employee: Employee) { println(employee.department?.head?.name) }
Safe calls are useful in chains
employee.department?.head?.name?.let { println(it) }
To print only for non-null values, you can use the safe call operator together with let
let
val text: String? = null
val length: Int? = text?.length() // returns length or null
equivalent to
val length: Int? = if (text != null) text.length() else null
Elvis operator ?:
val length: Int = text?.length() ?: 0 // returns length or 0 val length: Int = if (text != null) text.length() else 0
?:
If the expression to the left of ?: is not null, it is returnedotherwise, expression to the right is returned
Note that the expression on the RHS is evaluated only if the LHS is null
Note
The not-null assertion operator !! converts any value to a non-null type and throws an NPE exception if the value is null
!!
fun printDepartmentHead(employee: Employee) { println(employee.department!!.head!!.name!!) }
Please, avoid using unsafe calls!!
class
interface
fun Int.downTo(to: Int): IntProgression { return IntProgression.fromClosedRange(this, to, -1) }
receiver
this
10.downTo(1)
Invoked by stating value of receiver type, followed by . and function invocation
.
any type
val List<Int>.lastIndex : Int get() = this.size - 1 listOf(1, 2, 3).lastIndex
List<Int>
lastIndex
no access to private members
infix fun Int.downTo(to: Int): IntProgression { return IntProgression.fromClosedRange(this, to, -1) }
infix
10 downTo 1
Thanx!