Kotlin Basics Toggle navigation Mobile OS Development Lessons Intro Development Environment Hello World Kotlin Basics Kotlin Functions Kotlin Classes Android Code Organization LogCat Views Layouts Handling Screen Rotation Recycler View and Multi-Activity Apps Multi-Activity Apps Extras Spinners Activity Lifecycles Menus Activity Data Sharing Persisting Data To File Dialogs Intents Images Camera Threads and Coroutines Services Alarms Notifications RSS Feeds JSON Google Maps Networking with Volley MediaPlayer SoundPool Playing Video SMS Messaging Homeworks Homework 1 Homework 2 Homework 3 Assignments Assignment 1 Assignment 2 Assignment 3 Assignment 4 Project Project Admin Course Policy Coding Standards Calendar Intro Development Environment Hello World Kotlin Basics Kotlin Functions Kotlin Classes Android Code Organization LogCat Views Layouts Handling Screen Rotation Recycler View and Multi-Activity Apps Multi-Activity Apps Extras Spinners Activity Lifecycles Menus Activity Data Sharing Persisting Data To File Dialogs Intents Images Camera Threads and Coroutines Services Alarms Notifications RSS Feeds JSON Google Maps Networking with Volley MediaPlayer SoundPool Playing Video SMS Messaging Kotlin Basics You should bookmark the Kotlin reference website: https://kotlinlang.org/docs/reference/ Basic concepts In this lesson we'll cover several basic concepts in Kotlin, and you can try the code in REPL (Android Studio->Tools->Kotlin->Kotlin REPL). Variables Kotlin distinguishes between read-only variables, declared using "val", which can be assigned a value only once, and mutable variables, declared using "var", whose value can be reassigned:
val a:Int = 5 //read-only variable a of type Int, with value 5
val b = 2 //read-only variable b; type is inferred (Int)
val c:Double = 3.0 // read-only variable c of type Double, with value 3
var x = 5 //declare and initialize x (type is inferred)
x++ //increment x Notice above that in Kotlin there is no semicolon to end the statement (unlike Java and other languages). Let's note one thing about val. If your val variable value is a reference, then you cannot assign it a different reference later, but the content of the object could still change if the object is not immutable. Val only applies to the reference, and it doesn't make the object it points to immutable. For example:
val myList = mutableListOf("cat","mouse")
myList.remove("mouse") // this works
myList = mutableListOf("fish", "aquarium") //Error: myList was declared with val
val myList2 = listOf ("plant", "tree", "flower")
myList2.remove("flower") //Error: List is immutable In the example above, we could not re-assign the value of myList, but we could change the list itself (by removing an element for example). Comments Like in other languages, Kotlin uses // for single-line or end of line comments and /* */ for multi-line comments. Multi-line comments can be nested.
//this is a single-line comment
/* this is a multi-line
comment
/* this is a nested comment */
*/ Types See https://kotlinlang.org/docs/reference/basic-types.html for a basic description of types in Kotlin. Kotlin is a statically-typed language. Types are declared or inferred at compile time, and they do not change as the program executes. The built-in types to represent numbers are Byte, Short, Int, Long, Double, Float. Kotlin does not do implicit type casting, as it is considered a source of errors. If you do need it, type casting must be explicit.
val a:Int = 5
var x:Double = a //this will fail because "a" is of type Int and a Double is expected
var x:Double = a.toDouble() //this is OK One interesting thing about numerical literals, is that you can use underscore, _, to make long numbers easier to read:
val oneMillion = 1_000_000
val sampleSSN = 123_123_1234L Nullability We mentioned before how Kotlin helps avoid null ponter exceptions. Let's talk more about that. By default, when you declare a variable of some type, that variable cannot be null. If you want to allow null values in a variable, the type in the type declaration must be followed by the question mark symbol, ?, to specify that the variable can store null:
var students: Int = null //this does not work, since Int cannot be null
var students: Int? = null // this works since the type is now Int or null Operators +, -, /, *, % with the usual meaning. Dividing two integers gives an integer result, so 5/2 is 2. Even if numbers are stored as primitive types, you can call the corresponding operator method (plus, minus, times, div, and rem) on the number.
var x = 5 //inferred type Int
x.times(4) //result is 20
Kotlin has a few new operators related to treatment of nullable types in Kotlin: Safe call operator ?. Let's say you have a variable of type String? and you want to determine the length of the string. One way to do this is to check that the string is not null, and if so, use the lenght property of the strings to get the lenght. In Kotlin, you can also use the safe call operator
var s: String? = null
println(s?.length) s?.length returns length of s if s is not null, or null otherwise. One can "chain" safe call operators: e.g. student?.major?.seniorAcAdviser returns null if any of the properties in it is null (either student or student.major o student.major.seniorAcAdviser). Elvis operator ?: a ?: b returns a if a is not null, otherwise returns b
var s: String? = null
val len :Int = s?.length ?: -1 //if s?.lenght is not null, len is s.lenght, otherwise len is -1 Not-null assertion operator !! a!! will return a if a is not null, or will throw a null pointer exception if a is null We can see that null pointer expections are possible in Kotlin, but at least they are not unexpected.
var s: String? = null
val len :Int = s!!.length //this can throw a null pointer exception Strings Strings are implemented by class String. Like in Java, strings are immutable. You can access a character in a string by using the indexing operation s[i], where i is the index of that character, starting with 0. You can concatenate strings using the + operator. You can concatenate other types as welll, as long as the first operand is of type string:
val s1 = "Hello"
val s2 = "Kotlin"
println(s1[0]) //access the character at index o
println(s1 + " " + s2) //string concatenation
println(s1 + 2) //prints Hello2
for (c in s1){ //for loop to iterate through all the characters in a string
println(c)
} String templates Very useful in Kotlin are string templates, which often are preferred to string concatenation. String literals can contain template expressions, or pieces of code that will be evaluated and the value used in concatenation. Template expressions start with $, followed either by a variable name, or by an expression in curly braces.
val s1 = "Hello"
val s2 = "Kotlin"
println("Template use: $s1 $s2") // prints "Template use: Hello Kotlin"
println("$s1.length is ${s1.length}") //prints "Hello.length is 5"
Conditionals As expected, Kotlin has a Boolean data type with values true and false and boolean operators: || (lazy disjunction), && (lazy conjunction) and ! (negation). Once you know about booleans, you start wondering how to use them to test conditions. Kotlin has two basic sonditionals if-then and when. if-then is usually used for comparing values
if (numberCats > 2){
println("Many cats")
}
else{
println("Not that many cats")
} You can also use an if statement with ranges:
if (numberCats in 0..2)
println("Not that many cats") //note that {} not needed if only one statement
Unlike other languages, the if-then can be an expression, not just a statement (so it has a type and value). The value of the if-then expression is the value of the last expression in the if respectivelly else block (whichever is true).
val max = if (a > b) {
print("Choose a")
a
}
else { //else required if expression is assigned to a variable
print("Choose b")
b
} when is Kotlin's version of a switch statement. Like if-then, it can be used as an expression or as a statement. Here is an example of when statement. When used as expression, the value of the expression is the value of the satisfied branch and the else branch is required, unless the compiler can determine that all the possible cases are covered with branch conditions.
when (numberCats){
0 -> { println("no cats") } //block optional since only one statement
1,2 -> println("some cats") //can use comma between values
in 3..10-> println("more than a few cats") //can use ranges
else -> println("not sure") //else required if used as expression
} Branch conditions do not have to be constants, but can be arbitrary conditions. Moreover, if no argument is supplied to when, then branch conditions are just evaluated as boolean expressions and when can be used instead of nested if-then statements.
when {
x.rem(2) == 0 -> println("x is even")
else -> println("x is odd")
} Lists, arrays, and looping We already saw some example of lists, created usign mutableListOf() and listOf(). Arrays Arrays in Kotlin are represented by the Array class and they can be created in multiple ways. We can use the arrayOf() library function and pass the array values to it. Once created, the size of the array cannot be changed. Like in Java, C++, we can have multi-dimensional arrays since we can have arrays of arrays.
val myArray = arrayOf(11, 22, 33) //creates an Int array of size 3 [11,22,33]
val multiArray = arrayOf(myArray, intArrayOf(21,34)) //creates a 2D array To help with printing the contents of an array, the Arrays.toString(arrayHere) function can be used to provide a string representation of the array
import java.util.*
println(Arrays.toString(arrayOf(1,2,3))) //prints [1, 2, 3] Another way to create arrays is by using the Array constructor that takes the array size and a function that can return the initial value of each array element given its index. Don't worry too much about syntax yet, but note how compact the code is and how much boilerplate code is removed:
val array = Array (5) { i -> i * 2 } //creates [0, 2, 4, 6, 8] For loops Unlike Java or C, C++, there are no generic for loops in Kotlin. In Kotlin, for loops iterate through the elements of anything that provides an iterator (lists and arrays are some exaplmes of such collections). The basic for loop throug the elements of an array or list:
for (element in myArray) {
println(element)
} If you want to loop through an array or a list with an index, you can do it this way:
for (index in myArray.indices){
println(myArray[index])
} Or, if you want both the index and the value:
for ((index, element) in myArray.withIndex()) {
println("Value at $index is $element")
} We can use ranges as well:
for (i in 'b'..'g') print(i) //prints: bcdefg
for (i in 1..5) print(i) //prints: 12345
for (i in 5 downTo 2) print(i) //prints: 5432
for (i in 3..8 step 2) print(i) // prints: 357 While loops and do...while loops work as usual. Break and continue also work in Kotlin, with the usual effect. Exercises Select all of the statements below written with correct syntax. There may be more than one correct answer. Try them in REPL. val name : String = null val amount : Double? = 10.0 var myList : List? = listOf(null, null) myList?.size Solve the following using the operator methods in one line of code. If you start with 2 cats, and they breed twice, producing 11 offspring the first time, and 13 offspring the second time, and then you give away 7 cats, how many cats do you have left? How many litter boxes do you need if you consider 3 cats per litter box? Working with strings and nulls: Create a String variable rainbowColor, set its color value, then change it. Try to set rainbowColor to null. Declare two variables, greenColor and blueColor. Use two different ways of setting them to null. Create a nullable integer variable called nullTest, and set it to null. Use a null-check that increases the value by one if it's not null, otherwise returns 0, and prints the result. Hint 1: Use the Elvis operator. Hint 2: there is an inc() method that increments an integer Create three String variables for trout, bass, and snapper. Use a String template to print whether you do or don't like to eat these kinds of fish. Read the code below, and then select the correct array initialization that will display the corresponding output. val array = // initalize array here val sizes = arrayOf("byte", "kilobyte", "megabyte", "gigabyte", "terabyte", "petabyte", "exabyte") for ((i, value) in array.withIndex()) { println("1 ${sizes[i]} = ${value.toLong()} bytes") } Output: 1 byte = 1 bytes 1 kilobyte = 1000 bytes 1 megabyte = 1000000 bytes 1 gigabyte = 1000000000 bytes 1 terabyte = 1000000000000 bytes 1 petabyte = 1000000000000000 bytes 1 exabyte = 1000000000000000000 bytes Which of the statements below is the correct way to initialize the array above to give the correct output? val array = Array(7) val array = {1000} val array = Array(7){i -> 1000.0.pow(i)} //pow is a function in kotlin.math.pow val array = Array(7){i -> 1000.0.pow(10)} //pow is a function in kotlin.math.pow United States Naval Academy Department of Computer Science