
This is a chapter from the book Kotlin Essentials. You can find it on LeanPub or Amazon. It is also available as a course.
Int, Boolean, or String. Literals are built-in notations that are used to create instances. Some literal examples are a string literal, which is text in quotation marks, or an integer literal, which is a bare number.- numbers (
Int,Long,Double,Float,Short,Byte), - booleans (
Boolean), - characters (
Char), - strings (
String).
Int, Long, Byte, and Short.|---------|-------------|------------|---------------|
|
Byte | 8 | -128 | 127 ||
Short | 16 | -32768 | 32767 ||
Int | 32 | $-2^{31}$ | $2^{31} - 1$ ||
Long | 64 | $-2^{63}$ | $2^{63} - 1$ |Float and Double.|----------|-------------|------------------|---------------|----------------|
|
Float | 32 | 24 | 8 | 6-7 ||
Double | 64 | 53 | 11 | 15-16 |Int. A plain number with a decimal point is interpreted as a Double.
Long by using the L suffix after the number. Long is also used for number literals that are too big for Int.
Float by ending a number with the F or f suffix.
Byte or Short types. However, a number explicitly typed as one of these types will create an instance of this type. This also works for Long.fun main() { val b: Byte = 123 val s: Short = 345 val l: Long = 345 }
Byte or Long where Int is expected.
toInt or toLong.fun main() { val b: Byte = 123 val l: Long = 123L val i: Int = 123 val i1: Int = b.toInt() val i2: Int = l.toInt() val l1: Long = b.toLong() val l2: Long = i.toLong() }
_ between digits. This character is ignored, but we sometimes use it to format long numbers for better readability.fun main() { val million = 1_000_000 println(million) // 1000000 }
0x. To define a number using the binary numeral system, start it with 0b. The octal numeral system is not supported.fun main() { val hexBytes = 0xA4_D6_FE_FE println(hexBytes) // 2765553406 val bytes = 0b01010010_01101101_11101000_10010010 println(bytes) // 1382934674 }
Number type.fun main() { val i: Int = 123 val b: Byte = 123 val l: Long = 123L val n1: Number = i val n2: Number = b val n3: Number = l }
Number type specifies transformation functions: from the current number to any other basic type representing a number.abstract class Number { abstract fun toDouble(): Double abstract fun toFloat(): Float abstract fun toLong(): Long abstract fun toInt(): Int abstract fun toChar(): Char abstract fun toShort(): Short abstract fun toByte(): Byte }
to{new type} function. Such functions are known as conversion functions.fun main() { val b: Byte = 123 val l: Long = b.toLong() val f: Float = l.toFloat() val i: Int = f.toInt() val d: Double = i.toDouble() println(d) // 123.0 }
- addition (
+), - subtraction (
-), - multiplication (
*), - division (
/).
fun main() { val i1 = 12 val i2 = 34 println(i1 + i2) // 46 println(i1 - i2) // -22 println(i1 * i2) // 408 println(i1 / i2) // 0 val d1 = 1.4 val d2 = 2.5 println(d1 + d2) // 3.9 println(d1 - d2) // -1.1 println(d1 * d2) // 3.5 println(d1 / d2) // 0.5599999999999999 }
Notice, that the correct result of1.4 / 2.5should be0.56, not0.5599999999999999. This problem will be addressed soon.
Int by an Int, the result is also Int, so the decimal part is lost.fun main() { println(5 / 2) // 2, not 2.5 }
fun main() { println(5.toDouble() / 2) // 2.5 }
%:fun main() { println(1 % 3) // 1 println(2 % 3) // 2 println(3 % 3) // 0 println(4 % 3) // 1 println(5 % 3) // 2 println(6 % 3) // 0 println(7 % 3) // 1 println(0 % 3) // 0 println(-1 % 3) // -1 println(-2 % 3) // -2 println(-3 % 3) // 0 }
var:+=, wherea += bis the equivalent ofa = a + b,-=, wherea -= bis the equivalent ofa = a - b,*=, wherea *= bis the equivalent ofa = a * b,/=, wherea /= bis the equivalent ofa = a / b,%=, wherea %= bis the equivalent ofa = a % b,- post-incrementation and pre-incrementation
++, which increment variables value by1, - post-decrementation and pre-decrementation
--, which decrement variables value by1.
fun main() { var i = 1 println(i) // 1 i += 10 println(i) // 11 i -= 5 println(i) // 6 i *= 3 println(i) // 18 i /= 2 println(i) // 9 i %= 4 println(i) // 1 // Post-incrementation // increments value and returns the previous value println(i++) // 1 println(i) // 2 // Pre-incrementation // increments value and returns the new value println(++i) // 3 println(i) // 3 // Post-decrementation // decrements value and returns the previous value println(i--) // 3 println(i) // 2 // Pre-decrementation // decrements value and returns the new value println(--i) // 1 println(i) // 1 }
andkeeps only bits that have1in the same binary positions in both numbers.orkeeps only bits that have1in the same binary positions in one or both numbers.xorkeeps only bits that have exactly one1in the same binary positions in both numbers.shlshifts the left value left by the right number of bits.shrshifts the left value right by the right number of bits, filling the leftmost bits with copies of the sign bit.ushrshifts the left value right by the right number of bits, filling the leftmost bits with zeros.
fun main() { println(0b0101 and 0b0001) // 1, that is 0b0001 println(0b0101 or 0b0001) // 5, that is 0b0101 println(0b0101 xor 0b0001) // 4, that is 0b0100 println(0b0101 shl 1) // 10, that is 0b1010 println(0b0101 shr 1) // 2, that is 0b0010 println(0b0101 ushr 1) // 2, that is 0b0010 }
fun main() { println(0.1 + 0.2) // 0.30000000000000004 println(2147483647 + 1) // -2147483648 }
BigInteger, which represents a number without a decimal part. For unlimited size and precision, we should use the BigDecimal, which represents a number that has a decimal part. Both can be created using constructors[^04_1], factory functions (like valueOf), or a conversion from basic types that represent numbers (toBigDecimal and toBigInteger methods).import java.math.BigDecimal import java.math.BigInteger fun main() { val i = 10 val l = 10L val d = 10.0 val f = 10.0F val bd1: BigDecimal = BigDecimal(123) val bd2: BigDecimal = BigDecimal("123.00") val bd3: BigDecimal = i.toBigDecimal() val bd4: BigDecimal = l.toBigDecimal() val bd5: BigDecimal = d.toBigDecimal() val bd6: BigDecimal = f.toBigDecimal() val bi1: BigInteger = BigInteger.valueOf(123) val bi2: BigInteger = BigInteger("123") val bi3: BigInteger = i.toBigInteger() val bi4: BigInteger = l.toBigInteger() }
BigDecimal and BigInteger also support basic mathematical operators:import java.math.BigDecimal import java.math.BigInteger fun main() { val bd1 = BigDecimal("1.2") val bd2 = BigDecimal("3.4") println(bd1 + bd2) // 4.6 println(bd1 - bd2) // -2.2 println(bd1 * bd2) // 4.08 println(bd1 / bd2) // 0.4 val bi1 = BigInteger("12") val bi2 = BigInteger("34") println(bi1 + bi2) // 46 println(bi1 - bi2) // -22 println(bi1 * bi2) // 408 println(bi1 / bi2) // 0 }
Boolean, which has two possible values: true and false.fun main() { val b1: Boolean = true println(b1) // true val b2: Boolean = false println(b2) // false }
- Is the user an admin?
- Has the user accepted the cookies policy?
- Are two numbers identical?
Boolean is often a result of equality comparison. In Kotlin, we compare two objects for equality using the double equality sign ==. To check if two objects are not equal, we use the non-equality sign !=.fun main() { println(10 == 10) // true println(10 == 11) // false println(10 != 10) // false println(10 != 11) // true }
Comparable interface) can also be compared with >, <, >=, and <=.fun main() { println(10 > 10) // false println(10 > 11) // false println(11 > 10) // true println(10 < 10) // false println(10 < 11) // true println(11 < 10) // false println(10 >= 10) // true println(10 >= 11) // false println(11 >= 10) // true println(10 <= 10) // true println(10 <= 11) // true println(11 <= 10) // false }
- and
&&, which returnstruewhen the value on both its sides istrue; otherwise, it returnsfalse. - or
||, which returnstruewhen the value on either of its sides istrue; otherwise, it returnsfalse. - not
!, which turnstrueintofalse, andfalseintotrue.
fun main() { println(true && true) // true println(true && false) // false println(false && true) // false println(false && false) // false println(true || true) // true println(true || false) // true println(false || true) // true println(false || false) // false println(!true) // false println(!false) // true }
Boolean (or any other type), so logical operators should be used only with objects of type Boolean.Char type. We specify a character using apostrophes.fun main() { println('A') // A println('Z') // Z }
code property.fun main() { println('A'.code) // 65 }
\u, and then we need to use hexadecimal format, just like in Java.fun main() { println('\u00A3') // £ }
" or triple quotation marks """.fun main() { val text1 = "ABC" println(text1) // ABC val text2 = """DEF""" println(text2) // DEF }
\n. This is not the only thing that needs (or might need) a backslash to be expressed in a string.|-----------------|-----------------------|
|
\t | Tab ||
\b | Backspace ||
\r | Carriage return ||
\f | Form feed ||
\n | Newline ||
\' | Single quotation mark ||
\" | Quotation mark ||
\\ | Backslash ||
\$ | Dollar |fun main() { val text1 = "Let\'s say:\n\"Hooray\"" println(text1) // Let's say: // "Hooray" val text2 = """Let\'s say:\n\"Hooray\"""" println(text2) // Let\'s say:\n\"Hooray\" val text3 = """Let's say: "Hooray"""" println(text3) // Let's say: // "Hooray" }
trimIndent function, which ignores a constant number of spaces for each line.fun main() { val text = """ Let's say: "Hooray" """.trimIndent() println(text) // Let's say: // "Hooray" val description = """ A B C """.trimIndent() println(description) // A // B // C }
$) and consists of either a variable name (like "text is $text") or an expression in curly braces (like "1 + 2 = ${1 + 2}").fun main() { val name = "Cookie" val surname = "DePies" val age = 6 val fullName = "$name $surname ($age)" println(fullName) // Cookie DePies (6) val fullNameUpper = "${name.uppercase()} ${surname.uppercase()} ($age)" println(fullNameUpper) // COOKIE DEPIES (6) val description = """ Name: $name Surname: $surname Age: $age """.trimIndent() println(description) // Name: Cookie // Surname: DePies // Age: 6 }
fun main() { val text1 = """ABC\nDEF""" println(text1) // ABC\nDEF val text2 = """ABC${"\n"}DEF""" println(text2) // ABC // DEF }
\u, and then specifying a Unicode character code in hexadecimal syntax.
+ operator to concatenate two strings, so to create a new string that is a combination of two other strings. You can also use this operator to concatenate a string with any other object, which will be converted to a string. The result is always a string.fun main() { val text1 = "ABC" val text2 = "DEF" println(text1 + text2) // ABCDEF println(text1 + 123) // ABC123 println(text1 + true) // ABCtrue }
- Numbers that are represented by types
Int,Long,Double,Float,Short, andByteare created with bare number values with possible suffixes for type customization. We can define negative numbers or decimal parts. We can also use underscores for nicer number formatting. - Boolean values
trueandfalseare represented by theBooleantype. - Characters, which are represented by the
Chartype. We define a character value using single quotation marks. - Strings, which are used to represent text, are represented by the
Stringtype. Each string is just a series of characters. We define strings inside double quotation marks.
[^04_2]: This operator is similar to modulo. Both the remainder and the modulo operations act the same for positive numbers but differently for negative numbers. The result of -5 remainder 4 is -1 because -5 = 4 * (-1) + (-1). The result of -5 modulo 4 is 3 because -5 = 4 * (-2) + 3.
