Summary 1
This atom summarizes and reviews the atoms in Section I, starting at Hello, World! and ending with Expressions & Statements.
If you’re an experienced programmer, this should be your first atom. New programmers should read this atom and perform the exercises as a review of Section I.
If anything isn’t clear to you, study the associated atom for that topic (the sub-headings correspond to atom titles).
Hello, World
Kotlin supports both // single-line comments, and /*-to-*/ multiline
comments. A program’s entry point is the function main():
// Summary1/Hello.kt
fun main() {
println("Hello, world!")
}
/* Output:
Hello, world!
*/
The first line of each example in this book is a comment containing the name of
the atom’s subdirectory, followed by a / and the name of the file. You can
find all the extracted code examples through
AtomicKotlin.com.
println() is a standard library function which takes a single String
parameter (or a parameter that can be converted to a String). println()
moves the cursor to a new line after displaying its parameter, while print()
leaves the cursor on the same line.
Kotlin does not require a semicolon at the end of an expression or statement. Semicolons are only necessary to separate multiple expressions or statements on a single line.
var & val, Data Types
To create an unchanging identifier, use the val keyword followed by the
identifier name, a colon, and the type for that value. Then add an equals sign
and the value to assign to that val:
val identifier: Type = initialization
Once a val is assigned, it cannot be reassigned.
Kotlin’s type inference can usually determine the type automatically, based on the initialization value. This produces a simpler definition:
val identifier = initialization
Both of the following are valid:
val daysInFebruary = 28
val daysInMarch: Int = 31
A var (variable) definition looks the same, using var instead of val:
var identifier1 = initialization
var identifier2: Type = initialization
Unlike a val, you can modify a var, so the following is legal:
var hoursSpent = 20
hoursSpent = 25
However, the type can’t be changed, so you get an error if you say:
hoursSpent = 30.5
Kotlin infers the Int type when hoursSpent is defined, so it won’t accept
the change to a floating-point value.
Functions
Functions are named subroutines:
fun functionName(arg1: Type1, arg2: Type2, ...): ReturnType {
// Lines of code ...
return result
}
The fun keyword is followed by the function name and the parameter list in
parentheses. Each parameter must have an explicit type because Kotlin cannot
infer parameter types. The function itself has a type, defined in the same way
as for a var or val (a colon followed by the type). A function’s type is the
type of the returned result.
The function signature is followed by the function body contained within curly
braces. The return statement provides the function’s return value.
You can use an abbreviated syntax when the function consists of a single expression:
fun functionName(arg1: Type1, arg2: Type2, ...): ReturnType = result
This form is called an expression body. Instead of an opening curly brace, use an equals sign followed by the expression. You can omit the return type because Kotlin infers it.
Here’s a function that produces the cube of its parameter, and another that adds
an exclamation point to a String:
// Summary1/BasicFunctions.kt
fun cube(x: Int): Int {
return x * x * x
}
fun bang(s: String) = s + "!"
fun main() {
println(cube(3))
println(bang("pop"))
}
/* Output:
27
pop!
*/
cube() has a block body with an explicit return statement. bang() is an
expression body producing the function’s return value. Kotlin infers bang()’s
return type to be String.
Booleans
For Boolean algebra, Kotlin provides operators such as:
-
!(not) logically negates the value (turnstruetofalseand vice-versa). -
&&(and) returnstrueonly if both conditions aretrue. -
||(or) returnstrueif at least one of the conditions istrue.
// Summary1/Booleans.kt
fun main() {
val opens = 9
val closes = 20
println("Operating hours: $opens - $closes")
val hour = 6
println("Current time: " + hour)
val isOpen = hour >= opens && hour < closes
println("Open: " + isOpen)
println("Not open: " + !isOpen)
val isClosed = hour < opens || hour >= closes
println("Closed: " + isClosed)
}
/* Output:
Operating hours: 9 - 20
Current time: 6
Open: false
Not open: true
Closed: true
*/
isOpen’s initializer uses && to test whether both conditions are true.
The first condition hour >= opens is false, so the result of the entire
expression becomes false. The initializer for isClosed uses ||, producing
true if at least one of the conditions is true. The expression hour <
opens is true, so the whole expression is true.
if Expressions
Because if is an expression, it produces a result. This result can be
assigned to a var or val. Here, you also see the use of the else keyword:
// Summary1/IfResult.kt
fun main() {
val result = if (99 < 100) 4 else 42
println(result)
}
/* Output:
4
*/
Either branch of an if expression can be a multiline block of code surrounded
by curly braces:
// Summary1/IfExpression.kt
fun main() {
val activity = "swimming"
val hour = 10
val isOpen = if (
activity == "swimming" ||
activity == "ice skating") {
val opens = 9
val closes = 20
println("Operating hours: " +
opens + " - " + closes)
hour >= opens && hour < closes
} else {
false
}
println(isOpen)
}
/* Output:
Operating hours: 9 - 20
true
*/
A value defined inside a block of code, such as opens, is not accessible
outside the scope of that block. Because they are defined globally to the
if expression, activity and hour are accessible inside the if
expression.
The result of an if expression is the result of the last expression of the
chosen branch. Here, it’s hour >= opens && hour <= closes which is true.
String Templates
You can insert a value within a String using String templates. Use a $
before the identifier name:
// Summary1/StrTemplates.kt
fun main() {
val answer = 42
println("Found $answer!") // [1]
val condition = true
println(
"${if (condition) 'a' else 'b'}") // [2]
println("printing a $1") // [3]
}
/* Output:
Found 42!
a
printing a $1
*/
-
[1]
$answersubstitutes the value contained inanswer. -
[2]
${if(condition) 'a' else 'b'}evaluates and substitutes the result of the expression inside${}. -
[3] If the
$is followed by anything unrecognizable as a program identifier, nothing special happens.
Use triple-quoted Strings to store multiline text or text with special
characters:
// Summary1/ThreeQuotes.kt
fun json(q: String, a: Int) = """{
"question" : "$q",
"answer" : $a
}"""
fun main() {
println(json("The Ultimate", 42))
}
/* Output:
{
"question" : "The Ultimate",
"answer" : 42
}
*/
You don’t need to escape special characters like " within a triple-quoted
String. (In a regular String you write \" to insert a double quote). As
with normal Strings, you can insert an identifier or an expression using $
inside a triple-quoted String.
Number Types
Kotlin provides integer types (Int, Long) and floating point types
(Double). A whole number constant is Int by default and Long if you
append an L. A constant is Double if it contains a decimal point:
// Summary1/NumberTypes.kt
fun main() {
val n = 1000 // Int
val l = 1000L // Long
val d = 1000.0 // Double
println("$n $l $d")
}
/* Output:
1000 1000 1000.0
*/
An Int holds values between -231 and +231-1. Integral
values can overflow; for example, adding anything to Int.MAX_VALUE produces
an overflow:
// Summary1/Overflow.kt
fun main() {
println(Int.MAX_VALUE + 1)
println(Int.MAX_VALUE + 1L)
}
/* Output:
-2147483648
2147483648
*/
In the second println() statement we append L to 1, forcing the whole
expression to be of type Long, which avoids the overflow. (A Long can hold
values between -263 and +263-1).
When you divide an Int with another Int, Kotlin produces an Int result,
and any remainder is truncated. So 1/2 produces 0. If a Double is
involved, the Int is promoted to Double before the operation, so 1.0/2
produces 0.5.
You might expect d1 in the following to produce 3.4:
// Summary1/Truncation.kt
fun main() {
val d1: Double = 3.0 + 2 / 5
println(d1)
val d2: Double = 3 + 2.0 / 5
println(d2)
}
/* Output:
3.0
3.4
*/
Because of evaluation order, it doesn’t. Kotlin first divides 2 by 5, and
integer math produces 0, yielding an answer of 3.0. The same evaluation
order does produce the expected result for d2. Dividing 2.0 by 5
produces 0.4. The 3 is promoted to a Double because we add it to a
Double (0.4), which produces 3.4.
Understanding evaluation order helps you to decipher what a program does, both with logical operations (Boolean expressions) and with mathematical operations. If you’re unsure about evaluation order, use parentheses to force your intention. This also makes it clear to those reading your code.
Repetition with while
A while loop continues as long as the controlling Boolean-expression
produces true:
while (Boolean-expression) {
// Code to be repeated
}
The Boolean expression is evaluated once at the beginning of the loop and again before each further iteration.
// Summary1/While.kt
fun testCondition(i: Int) = i < 100
fun main() {
var i = 0
while (testCondition(i)) {
print(".")
i += 10
}
}
/* Output:
..........
*/
Kotlin infers Boolean as the result type for testCondition().
The short versions of assignment operators are available for all mathematical
operations (+=, -=, *=, /=, %=). Kotlin also supports the increment
and decrement operators ++ and --, in both prefix and postfix form.
while can be used with the do keyword:
do {
// Code to be repeated
} while (Boolean-expression)
Rewriting While.kt:
// Summary1/DoWhile.kt
fun main() {
var i = 0
do {
print(".")
i += 10
} while (testCondition(i))
}
/* Output:
..........
*/
The sole difference between while and do-while is that the body of the
do-while always executes at least once, even if the Boolean expression
produces false the first time.
Looping & Ranges
Many programming languages index into an iterable object by stepping through
integers. Kotlin’s for allows you to take elements directly from iterable
objects like ranges and Strings. For example, this for selects each character
in the String "Kotlin":
// Summary1/StringIteration.kt
fun main() {
for (c in "Kotlin") {
print("$c ")
// c += 1 // error:
// val cannot be reassigned
}
}
/* Output:
K o t l i n
*/
c can’t be explicitly defined as either a var or val—Kotlin
automatically makes it a val and infers its type as Char (you can provide
the type explicitly, but in practice this is rarely done).
You can step through integral values using ranges:
// Summary1/RangeOfInt.kt
fun main() {
for (i in 1..10) {
print("$i ")
}
}
/* Output:
1 2 3 4 5 6 7 8 9 10
*/
Creating a range with .. includes both bounds, but until excludes the top
endpoint: 1 until 10 is the same as 1..9. You can specify an increment
value using step: 1..21 step 3.
The in Keyword
The same in that provides for loop iteration also allows you to check
membership in a range. !in returns true if the tested value isn’t in the
range:
// Summary1/Membership.kt
fun inNumRange(n: Int) = n in 50..100
fun notLowerCase(ch: Char) = ch !in 'a'..'z'
fun main() {
val i1 = 11
val i2 = 100
val c1 = 'K'
val c2 = 'k'
println("$i1 ${inNumRange(i1)}")
println("$i2 ${inNumRange(i2)}")
println("$c1 ${notLowerCase(c1)}")
println("$c2 ${notLowerCase(c2)}")
}
/* Output:
11 false
100 true
K true
k false
*/
in can also be used to test membership in floating-point ranges, although
such ranges can only be defined using .. and not until.
Expressions & Statements
The smallest useful fragment of code in most programming languages is either a statement or an expression. These have one basic difference:
- A statement changes state.
- An expression expresses.
That is, an expression produces a result, while a statement does not. Because it doesn’t return anything, a statement must change the state of its surroundings (that is, create a side effect) to do anything useful.
Almost everything in Kotlin is an expression:
val hours = 10
val minutesPerHour = 60
val minutes = hours * minutesPerHour
In each case, everything to the right of the = is an expression, which
produces a result that is assigned to the identifier on the left.
Functions like println() don’t seem to produce a result, but because they are
still expressions, they must return something. Kotlin has a special Unit
type for these:
// Summary1/UnitReturn.kt
fun main() {
val result = println("returns Unit")
println(result)
}
/* Output:
returns Unit
kotlin.Unit
*/
Experienced programmers should go to Summary 2 after working the exercises for this atom.
Exercises and solutions can be found at www.AtomicKotlin.com.