Starting up with Kotlin
From Java to Kotlin
-
The Kotlin code is compiled to Java bytecode.
-
Java to Kotlin converter tool is available in IntelliJ
-
Adding a
datamodifier to a class automatically generates several methods for it:equalshashCodetoString
-
Example of data class:
data class Person(val name: String, val age: Int) -
No need to use
newkeyword in Kotlin- Example:
val person = Person("Alice", 27)
println(person.name) -
Kotlin syntax helps reduce redundancy.
- Example of
updateWeatherfunction in Kotlin:
fun updateWeather(degrees: Int){
// Using Pair shortcut syntax
val(description: String, color: Color) =
if(degrees < 10){
Pair("cold", BLUE)
} else if( degrees < 25) {
Pair("mild", ORANGE)
} else {
Pair("hot", RED)
}
...
}-
Could simplify it further by removing the data types because Kotlin can infer it.
fun updateWeather(degrees: Int){
// Using Pair shortcut syntax
val(description, color) =
if(degrees < 10){
Pair("cold", BLUE)
} else if( degrees < 25) {
Pair("mild", ORANGE)
} else {
Pair("hot", RED)
}
...
}-
Could simplify it further by using
wheninstead ofif:fun updateWeather(degrees: Int){
// Using Pair shortcut syntax and when clause
val(description, color) = when {
degrees < 10 -> Pair("cold", BLUE)
degrees < 25 -> Pair("mild", ORANGE)
else -> Pair("hot", RED)
}
...
} -
Could alternatively use
toinstead ofPair:fun updateWeather(degrees: Int){
// Using Pair shortcut syntax and when clause
val(description, color) = when {
degrees < 10 -> "cold" to BLUE
degrees < 25 -> "mild" to ORANGE
else -> "hot" to RED
}
...
}
-
- Example of
-
Does the automatic Java to Kotin Converter always produce the idiomatic Kotlin code?
- No, it doesn't
- The Java code likely will look very "Java" in the Kotlin version.
- You need to be immersed in Kotlin syntax to recognize how to manually reduce redundancies or to use "prettier" Kotlin syntax.
Basics
"Hello, world" Example
- Hello World:
package intro
fun main() {
val name = "Kotlin"
println("Hello, $name!")
}
- The
mainfunction can go with or without arguments- Example with argument:
fun main(args: Array<String>)
mainwith argument Example:
package intro
fun main(args: Array<String>){
val name = if(args.size > 0) args[0] else "Kotlin"
println("Hello, $name!")
}
- An
Array<String>in kotlin is the same array as in java. - In Kotlin,
ifelseclauses are expressions which can be assigned to a variable or returned in a function as shown above. - String templates utilize the
$sign next to a variable name to insert a value inside a string literal.
-
String templates work for variables and functions:
"String Text Here: $variable""String Text Here: ${functionCall(...)}"
-
What will be printed if no arguments are passed?
fun main(args: Array<String>) {
println("Hello, ${args.getOrNull(0)}!")
}Hello, null!will be printed
-
What will be printed?
fun foo(): String {
println("Calculating foo...")
return "foo"
}
fun main(args: Array<String>){
println("First ${foo()}, second ${foo()}")
}- This will be printed:
Calculating foo...
Calculating foo...
First foo, second foo
Variables
- An assigned-once and read-only variable will be denoted with
val- This is similar to a
finalvariable in java.
- This is similar to a
- A mutable variable will be denoted with
var - Types are inferred in Kotlin:
val greeting: String = "Hi!"does not need to explicitly say it is a String.val greeting = "Hi!"knows it is a String type.
- Why doesn't the following code compile?
var string = 1;
string = "abc";- Because you can't assign a string literal to a variable of Int type.
- Is it possible to modify an object stored in val?
- Yes
- There are no additional constraints.
- You can easily modify an object stored in
valas in Java. valis an immutable reference which doesn't say anything about the content that is stored.
- Example to demonstrate
valis a read-only reference and not an object.val languages = mutableListOf("Java")
languages.add("Kotlin") - Why doesn't the following code compile?
val list = listOf("Java")
list.add("Kotlin")- Because you can't modify a read-only list.
- Don't omit types
- Specify the types of a variable explicitly to improve clarity.
Functions
fun max(a: Int, b: Int): Int {
return if(a > b) a else b
}
is equivalent to:
fun max(a: Int, b: Int) = if (a > b) a else b
Unitis very similar tovoid- You don't really need to specify
Unitas the return type if you don't want to.
- You don't really need to specify
fun displayMax(a: Int, b: Int): Unit {
println(max(a,b))
}
- Is it possible to call a top-level function from Java? If yes, how?
- You call it as a static function of the class, which name corresponds to the file name.
Calling a top-level function from Java
MyFile.kt
package intro
fun foo() = 0
UsingFood.java
package other;
import intro.MyFileKt;
public class UsingFoo {
public static void main(String[] args){
MyFileKt.foo();
}
}
Named & default arguments
- What will be printed?
println(
listOf("a", "b", "c").joinToString(
separator = "",
prefix = "(",
postfix = ")"
)
);
- Output:
(abc)
Default Values
fun displaySeperator(character: Char = '*', size: Int = 10){
...
}
- Could assign a value inside the function parameters to specify the default values.
Named arguments
- Suppose you only want to specify the second parameter and keep the first one at its current default value.
fun displaySeperator(character: Char = '*', size: Int = 10){
...
}
...
displaySepartor(size = 5)
- The output will be:
*****
What will be printed?
fun displaySeperator(character: Char = '*', size: Int = 10){
repeat(size) {
print(character)
}
}
displaySepartor(3, '5')
- Answer: the code won't compile.
Control Structures
Conditionals: if & when
- Ternary operator does not exist in Kotlin
- Just use the if expression
- Example:
val max = if(a > b) a else b
- when expression:
enum class Color {
BLUE, ORANGE, RED
}
fun getDescription(color: Color): String =
when(color){
BLUE -> "cold"
ORANGE -> "mild"
RED -> "hot"
}
- Checking multiple values at once Example:
fun respondToInput(input: String) = when(input) {
// you can check mulitple values at once
"y", "yes" -> "Good Outcome"
"n", "no" -> "Bad Outcome"
else -> "I don't understand you"
}
- Any expression can be used as a branch Condition
- Example:
fun mix(c1: Color, c2: Color) =
when (setOf(c1, c2)) {
setOf(RED, YELLOW) -> ORANGE
setOf(YELLOW, BLUE) -> GREEN
setOf(BLUE, VIOLET) -> INDIGO
else -> throw Exception("Dirty color")
}
- Checking types
- Example:
when(pet) {
is Cat -> pet.meow()
is Dog -> pet.woof()
}
- Capturing when subject in a variable
- Example:
when(val pet = getMyFavoritePet()) {
is Cat -> pet.meow()
is Dog -> pet.woof()
}
- You should always replace if with when?
- False
Loops
- While loops:
while(condition){
...
}
- Do-while loops:
do{
...
} while(condition)
- for loop:
val list = listOf("a", "b", "c")
for (s in list){
print(s)
}
- Iterating over map
val map = mapOf (1 to "one", 2 to "two", 3 to "three")
for((key, value) in map){
println("$key = $value")
}
- Iterating over range
// including upper bound
for (i in 1..9){
print(i)
}
// excluding upper bound
for (i in 1 until 9){
print(i)
}
- Iterating over String
- Example:
- The output will be:
bcd
- The output will be:
for (ch in "abc") {
print(ch + 1);
}
- Rewrite the following java code into Kotlin:
for(char c = '0'; c < '9'; c++){
System.out.print(c);
}
- Kotlin version:
for(c in '0' until '9'){
print(c)
}
'in' checks & ranges
incould be used to check if an item belongs in something.- Example:
c in 'a'..'z'noteBoth the lower and upper bound is checked.
- Not
ina range Example:c !in '0'..'9' inaswhen-condition Example:fun recognize(c: Char) = when(c) {
in '0'..'9' -> "It's a digit!"
in 'a'..'z', in 'A'..'Z' -> "It's a letter!"
else -> "I don't know..."
}- What will be printed?
println("Kotin" in "Java".."Scala")
println("Kotlin" in setOf("Java", "Scala"))
- Answer:
true false- First one checks in alphabetical order. So, a word that starts with the letter 'K' like "Kotlin" lies between the words "Java" and "Scala" in alphabetical order.
Exceptions
throwis an expression
val percentage =
if(number in 0..100)
number
else
throw IllegalArgumentException(
"A percentage value must be" + "between 0 and 100: $number")
tryis an expression:
val number = try {
Integer.parseInt(string)
} catch (e: NumberFormatException){
return
}
@Throwsannotation
@Throws(IOException::class)
fun foo() {
throw IOException()
}
- Is there any difference between calling foo and bar from Java?
fun foo() {
throw IOException()
}
@Throws(IOException::class)
fun bar() {
throw IOException()
}
- Answer: The code calling
barcompiles, but the code callingfoodoesn't compile.
Extensions
Extension Functions
- The time that the function extends is called a Receiver.
- In the example below,
Stringis the receiver of thelastCharfunction. - In the body of this function, we can access the receiver by this reference.
thisrefers toString.
- In the example below,
fun String.lastChar() = this.get(this.length - 1)
this can actually be omitted, resulting in this alternative:
fun String.lastChar() = get(length - 1)
- You need to import the extension function to use it.
import com.example.util.lastChar
val c: Char = "abc".lastChar()
Calling Extension Functions from Java code
- Extension functions are like static functions in java
- Example java code:
import static StringExtensionKt.lastChar;
char c = lastChar("abc");
- How many arguments does the repeat function have if you call it from Java?
fun String.repeat(n: Int): String {
val sb = StringBuilder(n * length) for (i in 1..n) {
sb.append(this)
}
return sb.toString()
}
- Answer: 2
- Is it possible to call a private member of String inside an extension function to String?
fun String.lastChar() = get(length - 1)
- Answer: no
- You can't call a private member from a static function of another class in java.
- Kotlin extension functions are regular static functions defined in a separate auxiliary class.
- You cannot call private members from extensions.
Examples from the Standard Library
-
No Kotlin SDK
- Just JDK + Extensions
- Benefits:
- small runtime jar
- easy Java interop
- Benefits:
- Just JDK + Extensions
-
An example of an Extension function:
untilinfix fun Int.until(to: Int): IntRange
//to call it, two different syntaxes:
1.until(10)
1 until 10
Formatting multi-line strings
- Indentations can be caught in multi-line strings
- The code below uses the
trimMarginextension to address that.
- The code below uses the
val q = """To Code,
| or not to code?..""".trimMargin()
- Thus, the output becomes:
To Code,
or not to code?..
- Conversion to numbers:
- Example:
"123".toInt() "1e-10".toDouble()
- Example:
Custom extension functions
- You can create your own extension functions:
fun <T> T.eq(other: T?): Boolean {
return this == other
}
val a = "Hello"
val b = "Hello"
val c = "World"
println(a.eq(b)) // true
println(a.eq(c)) // false
- What is the type of
'a' to 1.0?- Answer:
Pair<Char, Double>
- Answer:
- Write
Sumas an extension function. - Answer:
fun List<Int>.sum(): Int {
var total = 0
for(i in this){
total += i
}
return total
}
fun main(args: Array<String>){
val ls = listOf(1, 2, 3)
val output = ls.sum()
println(output)
}
Calling Extensions
- What will be printed?
open class Parent
class Child: Parent()
fun Parent.foo() = "parent"
fun Child.foo() = "child"
fun main(args: Array<String>) {
val parent: Parent = Child()
println(parent.foo())
}
- Answer:
parent - What will be printed?
fun String.get(index: Int) = '*'
fun main(args: Array<String>) {
println("abc".get(1))
}
- Answer:
b