Declare and Reference Simple Variables
Variable declarations start with either val for immutable variables and var for mutable variables. Note that an immutable variable is not quite the same as a constant. A constant has its value set statically, in other words its value is specified in the source code. An immutable variable is declared without being initialised and it can subsequently have its value set statically or dynamically (for instance if the user is asked to provide the value used to initialise the variable.
However, once it has been set, it can not be changed again!
As was mentioned earlier, in most case you don’t need to specify a type since this is inferred by the compiler but you may do so. The type is placed after the variable name and is separated form it with a colon.
fun main(args: Array<String>) { var name = "Philip" println("Hi, $name. Welcome to Kotlin Essential Training!!") }
fun main(args: Array<String>) { var name:String = "Philip" println("Hi, $name. Welcome to Kotlin Essential Training!!") }
Figures 7 and 8 show two examples of a main function with one variable declaration with the identifier name and the value “Philip”. Note that in figure 7, the type is not specified but in figure 8 it has been specified as a String.
There are, if course, various types associated with number including Int and Float and a list of these can be found here. Note that a possible source of error is specifying an Int type as Integer which will give you an error like:
Error:(5, 23) Kotlin: The integer literal does not conform to the expected type Integer
This is because Integer is a class in Kotlin, as it is in Java so it is recognised as a type, but not one that you can use in this way.
It is also worth noting that if you specify a value such as 4.0, this is a float so if you don’t implicitly specify a type, Kotlin will assume this is the case. If you explicitly specify a type as Int, this will give you an error although the error message is reasonably clear and this is:
Error:(5, 19) Kotlin: The floating-point literal does not conform to the expected type Int
Similarly, and probably not as obvious, if you explicitly specify a variable as Float or Double and then try to assign it a value such as 4, you will get the opposite error:
Error:(7, 11) Kotlin: The integer literal does not conform to the expected type Double
However, you can, as in Java, use a cast such as toFloat() or toDouble() and similarly toInt() if necessary.
Kotlin Type Conversion 1. toByte() 2. toShort() 3. toInt() 4. toLong() 5. toFloat() 6. toDouble() 7. toChar()
See figure 9 for a list of type conversions in Kotlin. From what I can tell, Kotlin doesn’t like it if you try to apply toInt() directly to, for example, a float value so:
num = 4.3f.toInt()
will give you an error. If you want to convert this to an integer you would have to apply so you would do something like this:
var num2 = num.toInt()
Note, as can be seen in the above examples, a float value in Kotlin has an f at the end to differentiate it from a double.
If you want your variables to accept the default values, such as Int for 4 or float for 4.0, omitting the data type is fine but if you want other types such as Double or Long, these should be explicitly specified.
Data Types and Literal Expressions
Unlike Java, Kotlin does not have any primitives. Every type is a class with member functions, properties and so on. There are some built-in types to handle numbers, Booleans, characters, strings and arrays and these are similar to the Java primitive types.
Everything else is part of either the Kotlin standard library or Java’s runtime library.

Figure 10 shows a list of the numeric data types in Kotlin with the equivalent Java type shown in parentheses. The fact that these ae classes in Kotlin is reflected in the fact that they start with an upper case letter. In each case, the bit width is the same as the Java equivalent.
It is worth noting that while you can declare a variable in Kotlin without specifying a type, Kotlin infers the type from the initial value you provide it with. This means that if you do not specify an initial value, you must specify the type.
Kotlin has some special means of handling null values. You can specify that a variable is nullable (that it can be assigned a null value) and you do this with the nullable operator. This is placed at the end of the type so, for example, if you want a nullable integer, its type will be Int? rather than just Int. So the variable declaration might look something like this:
var num3: Int?
It can also be initialised at the same time and the value can be set to an appropriate value for the type as in:
var num3: Int? = 4
or, of course, it can be initialised with a null value as in:
var num3: Int? = null
Just as a matter of interest, if you use the variable in a print statement when its value is set to null as in:
println("The nullable variable has a value of $num3")
the value printed will be null as in:
The nullable variable has a value of null
If the nullable operator is omitted, then you must assign a value to the variable. In some cases, setting a variable to null must be done explicitly but sometimes it can be inferred by the compiler.
Creating variables of other types, generally this will mean creating instances of user-defined classes, the syntax is pretty similar. As in Java, you will make use of the classes constructor and the use of a constructor is the same as in Java but you can also specify, if necessary, the type of object using a semicolon followed by the class name of the object you want to create.
Note, however, that you don’t need the word new so if you want to create an object of class Person, for example, you would do it like this:
val p = Person()
As in Java, the number and types of any arguments you provide the constructor with depends on how this has been defined within the class.
Convert Numeric and String Types
If you consider the following snippet of code:
val myInt = 42 val myLong: Long = myInt
This would work in Java because the Long value is larger than the integer and so Java would automatically upcast the integer to a long. Kotlin does not do this so it gives an error. The value on myInt has been inferred to be an integer so we can not use it to initialise a long variable. Instead, we would have to explicitly cast the int value to long which we would do with:
val myLong: Long = myInt.toLong()
Kotlin can, like Java, determine the type of an object dynamically and we can do this, for instance, with a print statement such as:
println("The type of myLong is ${myLong::class.simpleName }")
and this will output something like:
The type of myLong is Long
We can replace simpleName with qualifiedName to get the completely qualified name of the class which in this case would give the output:
The type of myLong is kotlin.Long
This reflects the fact that the built in types are a part of a package which is simply called Kotlin and this is always available to a Kotlin application without the need to import anything.
var myFloat: Float = 42.9f var myInt = myFloat.toInt() println("The value of myInt is $myInt") var myDouble = 3.1428571428571428571428571428571 myFloat=myDouble.toFloat() println("The value of myDouble is $myDouble") println("The value of myFloat is $myFloat")
Finally, let’s consider the code shown in figure 11 above. Here, we are setting the value of a float to 42.9 and then casting that to an integer value. The cast simply removes the non-integer part of the float value.
Secondly, we are setting the value of a double to 3.1428571428571428571428571428571 and changing the value of our float variable to match this, so he we are casting from a double to a float.
Finally, let’s consider the code shown in figure 11 above. Here, we are setting the value of a float to 42.9 and then casting that to an integer value. The cast simply removes the non-integer part of the float value.
Secondly, we are setting the value of a double to 3.1428571428571428571428571428571 and changing the value of our float variable to match this, so he we are casting from a double to a float.
The value of myInt is 42 The value of myDouble is 3.142857142857143 The value of myFloat is 3.142857
The output for this code is shown in figure 12. This demonstrates two points.
• Downcasting will normally lead to a loss of precision. • Unlike Java, type conversions are always explicit which helps to make the code more readable
Compare Numeric Values
To compare values in Kotlin, we have two options and these are to use standard comparison operators or methods relating to the types.
val num1 = 10 val num2 = 15 val match = (num1 == num2) println("match = $match") val match2 = (num1.equals(num2)) println("match2 = $match2")
Figure 13 shows a snippet of code which makes use of these methods and as we might expect, in this case shows that match and match2 both evaluate to false. If we change the value of one of the numbers so that they are both the same, then both values evaluate to true.
On the face of it, these methods would therefore seem to be equivalent, however, IntelliJ provides a useful tool which allows us to see that in the background, they are quite different. From the Tools menu, we can select Kotlin and then Kotlin bytecode. The bytecode itself is not particularly helpful but there is a button we can click to decompile the code and this is decompiled to Java code.
Intrinsics.checkParameterIsNotNull(args, "args"); int num1 = 10; int num2 = 15; boolean match = false; String var4 = "match = " + match; System.out.println(var4); boolean match2 = Integer.valueOf(num1).equals(Integer.valueOf(num2)); String var5 = "match2 = " + match2; System.out.println(var5);
From this, we can see that when the code was compiled, the compiler checked the values of num1 and num2 and has calculated that match will evaluate to false so it has simply set the value to false. In other words, the bytecode is not bothering to do the comparison because, as it happens, the compiler can do this!
With match2, the process is different. There is a call to a static method (valueOf()) followed by a call to the equals() method and a second call to the static method.
From this, we can see that the comparison operator (==) is much more efficient than the call to the equals() method and so this should be the preferred method and used whenever possible.
You can also use the compareTo() method which is a bit more flexible. This method returns 0 if the values match but also returns a positive value if the value from which you are calling the function is greater and a negative value if the value from which you are calling the function is smaller. The syntax for this is:
println(num1.compareTo(num2))
Do Math With Operators
As with comparisons, we can also do basic arithmetic using the same standard arithmetical operators as you will find in C or Java but you can also use a method associated with the number.
val num1 = 10 val num2 = 15 val sum1 = num1 + num2 val sum2 = num1.plus(num2) println("Sum1 = $sum1") println("Sum 2 = $sum2")
We can see a comparison of these methods in figure 5 and as we would expect, in both cases the result is the same.
Again, as previously, we can inspect the bytecode and decompile it to Java but this time, we see that what is happening behind the scenes is identical.
Note that similar things apply for all the other regular arithmetical operators and also like, C or Java, we can use the increment or ++ operator (as well as the decrement or – operator). There is also a method inc() or dec().
var num1 = 10 println("Num1 = $num1") num1++ println("Num1 = $num1") num1.inc() println("Num1 = $num1")
Num1 = 10 Num1 = 11 Num1 = 11
In figure 16, there is some sample code which shows both the increment operator and the inc() method in use with the output resulting from running this in figure 17. These work as expected, but the output shows an unexpected result. Using the increment operator, the value of num1 has been incremented, but when we used the inc() method, it does not seem to have been incremented!
Looking at the decompiled bytecode, we can see that in the first instance, when the increment operator has been used, the bytecode decompiles to:
int num1 = num1 + 1;
The corresponding line for the inc() method is:
int var10000 = num1 + 1;
What we are seeing here is that the inc() method is using a temporary variable and what this tells us is that it does not affect the variable from which it is being called. To create and use an incremented value using inc(), we therefore need to assign the result to some variable and we can use this to actually increment num1 as follows:
num1 = num1.inc()
Alternatively, of course, we can assign the result to a new or other existing variable as in:
val num3 = num1.inc()
An interesting side note is that there is a potential error here if we have declared a variable with val which we subsequently try to increment. The problem, of course, is that we are trying to change an immutable element.
Assuming we want it to be mutable, we can either manually amend the declaration to var or we can use IntelliJ to fix it for us. If we hover over the variable name in the line with the increment operator, we can press Alt+Enter or the down arrow to the left of the ide and select “Make variable mutable”.
Finally, note that as with C or Java, we can use the increment operator as either a pre-increment or a post-increment operator so we can have something like:
++num1
Use Functions from kotlin.math
The built-in number classes have their own math functions and in addition, Kotlin inherits a lot of functions and constants from Java’s maths class.
Figure 18, demonstrates the use of the methods built into the number classes and these replace the single conventional operators (although Kotlin still has access to these). Like any method, of course, these are passed to an object with an appropriate argument.
Notice that for the division, the value of num1, which is an integer, is cast to a double and the value returned is a double.
fun main(args: Array<String>) { val num1 = 15 val num2 = 10 val sum = num1.plus(num2) println("sum=$sum") val difference = num1.minus(num2) println("difference=$difference") val product = num1.times(num2) println("product=$product") val quotient:Double = num1.toDouble().div(num2) println("quotient=$quotient") val remainder = num1.rem(num2) println("remainder=$remainder") }
We can also call Java’s mathematical operators using a fully qualified name so, for instance, to return the absolute value of a variable we can use the abs function as seen in figure 19.
val neg = -127 val pos = Math.abs(neg) println("The absolute value of $neg is $pos")
We can also use an import statement such as:
import java.lang.Math.abs
which we can use to import specific functions or more generally:
import java.lang.Math.*
if we want to import all of the math functions.
However, these methods are also exposed as Kotlin functions within Kotlin’s own math package but these are not imported automatically. In fact, if you use the auto-correct function within IntelliJ when you are using a method that has not been imported and for which you are not using the fully qualified name, IntelliJ will generate an import statement for this Kotlin math package which will look something like:
import kotlin.math.abs
Again, we can also use the wildcard to import all math functions. One interesting point here is that with this example, the import of the abs function, Kotlin will offer you a number of options since there are different abs methods for int values, double values and so on. The import statement is the same for each so it doesn’t matter which one you select (note, this applies for instance, if you put the cursor after the name of the method you need an import statement for and press control and space).
The math library also includes some constants and again, these can be imported individually as in:
import kotlin.math.PI
or as part of the whole library using the wildcard.
Build a String
We can create string variables in the same way as we create, for example, integer or double variables or we can create an empty string using the constructor for the String class. Some examples of this are shown in figure 20.
var empty = String() var notEmpty = String() notEmpty="Hello" var stillNotEmpty="Goodbye!" println("$empty") println("$notEmpty") print("stillNotEmpty")
However, as in Java, Kotlin strings are immutable. This means that if we want to create a long string by concatenating several smaller strings together, there is a danger that we are creating a lot of objects in the background that are not necessary.
Kotlin helps to avoid this problem via the StringBuilder class, which is actually a type alias. This means that the designers of Kotlin decided they were not able to improve on the way Java does things so this is utilising the Java class of the same name.
To illustrate this I have created a function in a new Kotlin file (this is to allow me to try writing a function outside of a class).
fun builder () { val builder = StringBuilder("To be or not to be\n") .append("that is the question\n") .append("Whether tis nobler in the mind\n") builder.append("to suffer the strings and arrows") val result = builder.toString() println(result) }
Figure 21 shows an example of the use of the StringBuilder class. Note that we can create a StringBuilder object and initialise it with a string parameter and we can also, as part of the expression, append some additional strings. We can also subsequently append additional strings if required.
We can then convert the StringBuilder object to a string (it would probably be more accurate to say that we can obtain the string from the StringBuilder object) by utilising its toString() method.
Note that in this case, the function which we have called builder() does not take any arguments or return any values so it can be called from within, lets say, the main class in our application with a simple method call:
builder()
Define Constants in a Companion Object
The const keyword in Kotlin is used to define a constant but it can be confusing to know when to use this rather than simply using an immutable variable.
Consider the code shown in figure 22.
val RED = "Red" fun main(args: Array<String>) { println("The colour is $RED") }
If we decompile the Kotlin bytecode we can see that a getter method has been created and we don’t really need this for a constant value. Note, also, that the variable itself is declared as private, static and final which is fairly normal for Java.
public final class MainKt { @NotNull private static final String RED = "Red"; @NotNull public static final String getRED() { return RED; } public static final void main(@NotNull String[] args) { Intrinsics.checkParameterIsNotNull(args, "args"); String var1 = "The colour is " + RED; System.out.println(var1); } }
Now, if we want to replace the immutable variable with a constant, the code will look something like that shown in figure 24.
const val RED = "Red" fun main(args: Array<String>) { println("The colour is $RED") }
Again, we can decompile the Kotlin bytecode to Java and the result of doing this is shown in figure 25.
public final class MainKt { @NotNull public static final String RED = "Red"; public static final void main(@NotNull String[] args) { Intrinsics.checkParameterIsNotNull(args, "args"); String var1 = "The colour is Red"; System.out.println(var1); } }
Here, we can see that there is no getter method. But also, if we compare the lines staring String var1 in both figure 23 and figure 25, we can see that where we used the const keyword, the compiler knows that this value can only be “Red” so it has gone ahead and created the string as “The color is Red”. In the previous version, it is appending the value of “Red” to the literal part of the string.
The consequence of this is that if we use the const keyword rather than using an immutable variable, the resulting code executes faster and uses less memory.
As a side note, this calls into question the value of having the option to use an immutable variable and the only reason I can think of for this is that it will (I think) allow you to declare the variable without a value and then later set it to some value (perhaps as the result of user input). So in effect, it still acts as a variable in the sense that we can assign it some value provided by the user but it is then subsequently not possible to amend the value or assign it a new value.
However, there is one problem with the way the constant is declared here. It is only available to any class created in the same file. To demonstrate this, we will add another class called Constants.
As things stand, we have a constant declared in our main file and we can try to reference the main class from our new class, Constants. Typing main does not work because the interpreter assumes that we are creating a main function. However, recall that main.kt compiles to MainKt and if we therefore use this as a starting point, we can reference anything that is accessible to Constants by typing MainKt followed by a dot. However, the variable $RED is not accessible so we cannot access it in this way. This demonstrates the fact that the top level file is not the place to put constants that you want to be accessible throughout your application.
So, in our Constants class, we will create something called a companion object which is available throughout the application.
class Constants { companion object { const val RED="Red" } }
Figure 26 shows an example companion object and we can access this from, for instance, out main.kt file by importing it with a statement such as:
import Constants.Companion.RED
In fact, the companions object is the place where you would put anything that you would think of as being static in Java and this can be either static constants or static methods.
We can decompile the Kotlin bytecode again and we see that the constant value is applied at compile time rather than run-time (that is, a statement such as println(“The colour is $RED”) decompiles to:
String var1 = "The colour is Red";
Another way to handle a constant in Kotlin is with an annotation called JvmField so instead of using const we use @JvmField. If we decompile this, we can see that the result is a little bit different and the same line of code in the previous paragraph decompiles to:
String var1 = "The colour is " + Constants.RED;
So this is slightly different but there is no getter method generated so the difference in performance is quite small.