Compiling programs |
We write programs in a high-level programming language like Kotlin, Java, C++, or C. A compiler translates the source code to object code (machine code).
For C and C++, it is customary to compile to native machine code. It can be executed directly by the processor. Native machine code is different for different processors, operating systems, and can depend on library versions. So a program compiled for Windows does not run on Mac OS, a program compiled for iOS does not run on Windows, and a program compiled for Mac OS 10.9 may not work on Mac OS 10.10.
Java and Kotlin are normally translated to object code for the JVM (Java virtual machine). A Java runtime environment is needed on the computer to execute the program. The exact same object code works on any system. JVM is heavily used on servers.
Until now we have written our programs as Kotlin scripts. Scripts are meant for quick programming tasks. Every time you run a script, it is compiled again and then executed.
For larger programs, it is better to write an application. It can consist of many separate source files that can be compiled individually (so you only have to compile the source file that you have changed). And once it has been compiled, an application starts much faster than a script.
Let's start with a small example. We make a source file point.kt (note the file extension kt, different from the extension kts we used for scripts):
data class Point(val x: Int, val y: Int) { override fun toString(): String = "[%d,%d]".format(x,y) }
I've made new directory that only contains this file now:
$ dir point.kt
I will now compile point.kt using the Kotlin compiler ktc:
$ ktc point.kt $ dir classes Point.class
As you can see, one new file has been created in the classes subdirectory, namely Point.class. A file with extension class contains exactly one class definition for the JVM.
We can now use this class from the interactive mode (or from a script):
$ ktc Welcome to Kotlin version 1.0.1-2 (JRE 1.7.0_95-b00) Type :help for help, :quit for quit >>> val p = Point(7, 13) >>> p [7,13]
Note that I did not define the Point class in this interactive session. Kotlin finds it automatically: When it sees the word Point which is currently not defined, it looks for the definition of a class or object with name Point. Therefore it checks all directories on its class path for files with the name Point.class. The ktc command adds the subdirectory classes to the class path, so Kotlin finds the file there and loads the class definition from that file. Note that the source file point.kt is not used—we can delete it and still use the Point class from Point.class.
When you write a script, you can place arbitrary Kotlin commands inside the file. For instance, this file hello.kt would be perfectly fine as a script:
// This cannot be compiled println("Hello World")
However, it cannot be compiled:
$ ktc hello.kt hello.kt:3:1: error: expecting a top level declaration
As the compiler already tells us, a source file can contain only top level declarations. This includes function definitions using fun, class definitions using class, and definitions of global variables using val and var.
This leaves the question: How can we actually run any code in our program if we are only allowed to put declarations? The answer goes back to the early 1970s: An application is started through a special function with the name main. It must take exactly one argument of type Array<String>, which will receive the command line arguments when the program starts.
So the following source file can be compiled hello-app.kt:
fun main(args: Array<String>) { println("Hello World") }
We compile it:
$ ktc hello-app.kt $ dir classes Hello_appKt.class META-INF
Note that a class file for a class Hello_appKt has been created (there is also a new subdirectory called META_INF that contains information for the compiler—we can ignore this entirely).
To run our program, we need to "run the class" Hello_appKt:
$ kt Hello_appKt Hello World
Remember that to run the program, you need to provide the class name, not the name of the source file. (And in fact the source file is not needed to run the program at all!)
Here is a small example of full application: number-game.kt:
import org.otfried.cs109.readString var secret = 0 val random = java.util.Random() fun answerGuess(guess: Int) { if (guess == secret) println("You got it") else if (guess > secret) println("Too big") else if (guess < secret) println("Too small") } fun main(args: Array<String>) { secret = random.nextInt(100) var guess = -1 while (guess != secret) { guess = readString("Guess my number> ").toInt() answerGuess(guess) } }
We can compile and run the program:
$ ktc number-game.kt $ dir classes META-INF Number_gameKt.class $ kt Number_gameKt Guess my number> 17 Too small Guess my number> 68 Too big Guess my number> 43 Too big Guess my number> 32 Too big Guess my number> 25 Too big Guess my number> 20 Too big Guess my number> 19 You got it
Note again that to run the program, you have to provide the name of the class Number_gameKt (which Kotlin has created automatically from the name of the source file number-game.kt).
A larger application will consist of several source files that define a number of functions and classes. At least one of the source files must define a function main(args: Array<String>) returning Unit. This function is the starting point of the program. The program is started through the class representing the source file containing the main function.
The easiest way to compile such an application is to compile all files at once:
$ ktc *.kt
However, when the program becomes larger, it makes sense to only recompile those parts that have changed, or that depend on the changed part. The easiest way to achieve this is to use a build tool such as gradle or maven, or an integrated development environment (IDE) such as IntelliJ (which has the best support for Kotlin, since it is made by the same company that created Kotlin). This tutorial will get you started.
Imagine you have written a nice program, and you want to give it to your friends or post it on your website. Obviously, you don't want them to have to install the Kotlin compiler just to run your program. So you have to package your program in such a form that all they need is a JVM installation (which most computers already have).
There are two things we need to do: First, instead of creating lots of class files, we will package them all together into a jar file. Second, we will add the Kotlin library into this package, so that it can be run without having Kotlin installed:
$ kotlinc -d number-game.jar -include-runtime number-game.ktThe compiler will create a new file number-game.jar. You can run this directly with the Java virtual machine:
$ java -jar number-game.jar Guess my number> 17 Too big Guess my number> 13 Too big Guess my number> 4 Too big Guess my number> 1 Too small Guess my number> 2 You got itThis will work on any computer that has the JVM installed. If your program has a graphical user interface, you can probably also start it by just clicking on the jar file.
Compiling programs |