Summary 1

This atom summarizes and reviews the atoms in Section I, from Hello, World! through Expressions & Statements.

If you’re an experienced programmer, this should be your first atom. Beginning programmers should read this atom and perform the exercises as review.

If any information here 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() which requires the following function signature:

// Summary1/Hello.kt fun main(args: Array<String>) { 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, a /, and the name of the file. You can find all the extracted code examples via AtomicScala.com.

Kotlin puts the type of an identifier after that identifier and a colon. The required argument (args) for main() is of type Array<String>.

println() is a standard library function which takes a single String argument (or an argument that can be converted to a String). println() moves the cursor to a new line after displaying its argument, while print() leaves the cursor on the same line.

Kotlin does not require a semicolon at the end of an expression, only to separate multiple expressions or statements on a single line.

val, var, & Data Types

To create an unchanging identifier, use the val keyword followed by the identifier name, a colon, and the type for that value. Next, use an equals sign and whatever you’re assigning to the 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

Thus, 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

The basic named subroutine in Kotlin is the function:

fun functionName(arg1: Type1, arg2: Type2, ...): ReturnType { // Lines of code ... return result }

The fun keyword is followed by the function name and the argument list in parentheses. Each argument must have an explicit type, because Kotlin cannot infer argument types. The function itself has a type, defined in the same way as a type for a var or val: a colon followed by the return type. A function’s type is the type of the returned result.

The function signature is followed by the function body surrounded by curly braces. The return statement provides the function’s return value.

You can use an abbreviated syntax if the function consists of a single expression:

fun functionName(arg1: Type1, arg2: Type2, ...): ReturnType = result

Instead of an opening curly brace, you see an equals sign followed by the result expression. When using this syntax you can omit the return type and the compiler will infer it.

Here’s a function that produces the cube of its argument, and another one 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(args: Array<String>) { println(cube(3)) println(bang("pop")) } /* Output: 27 pop! */

cube() has a block body with an explicit return statement. bang()’s body is a single expression that produces the function’s return value. The String return type of bang() is inferred by the compiler.

Booleans

Kotlin provides standard operators from Boolean algebra:

// Summary1/Booleans.kt fun main(args: Array<String>) { val hour = 6 val opens = 9 val closes = 20 println("Operating hours: $opens - $closes") 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 Open: false Not open: true Closed: true */

Look at isOpen’s initializer—the && checks to see whether both conditions are true. Since the first condition hour >= opens is false (6 am is too early), the result of the whole expression becomes false. On the contrary, isClosed is true if at least one of the conditions is true. Because hour < opens is true, the whole condition is true.

if Expressions

An if expression produces a result, which can be assigned to a var or val. Here you also see the use of the else keyword:

// Summary1/IfResult.kt fun main(args: Array<String>) { val result = if (99 < 100) 4 else 42 println(result) } /* Output: 4 */

Either branch of a if expression can be a multiline block of code, surrounded by curly braces:

// Summary1/IfExpression.kt fun main(args: Array<String>) { 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 the 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(args: Array<String>) { 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 */

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(args: Array<String>) { 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 distinguishes integer types (Int, Long) and floating point types (Double). A whole number constant is Int by default, Long if you append an L, and Double if it contains a decimal point:

// Summary1/NumberTypes.kt fun main(args: Array<String>) { 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(args: Array<String>) { 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.

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(args: Array<String>) { val d1: Double = 3.0 + 2 / 5 println(d1) val d2: Double = 3 + 2.0 / 5 println(d2) } /* Output: 3.0 3.4 */

It doesn’t: Because of evaluation order, 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. 2.0 divided 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 not sure what order Kotlin will evaluate expressions, use parentheses to force your intention. This also makes it clear to those reading your code.

Repetition with while

The while keyword loops as long as a controlling Boolean-expression continues to produce 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 through the block.

// Summary1/While.kt fun testCondition(i: Int) = i < 100 fun main(args: Array<String>) { var i = 0 while (testCondition(i)) { print(".") i += 10 } } /* Output: .......... */

The compiler infers Boolean as the result type for test_condition().

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(args: Array<String>) { 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 and Ranges

Many programming languages containing the for keyword often index into an iterable object by counting through integers. Instead, Kotlin’s for focuses on iterable objects like ranges and strings. For example, this for selects each character in the string "Kotlin":

// Summary1/StringIteration.kt fun main(args: Array<String>) { 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.

You can step through integral values using ranges:

// Summary1/RangeOfInt.kt fun main(args: Array<String>) { 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 tells 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(args: Array<String>) { 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.

Expression & Statements

The smallest useful fragment of code in most programming languages is either a statement or an expression. These have one simple 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.

Some 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(args: Array<String>) { 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.

Previous          Next

©2018 Mindview LLC. All Rights Reserved.