The Philosophy of Kotlin

There are three editions of Kotlin:

	•	Kotlin for the Java VM compiles to Java bytecode
	•	Kotlin JS complies to Javascript
	•	Kotlin Native compiles to native binaries (at the time the course was recorded, this edition was still experimental)

One of the major goals of Kotlin is to be concise so it eliminates a lot of the declarations found in Java. In addition, it removes some of the redundant key words, individual characters and so on found in Java and introduces some new keywords that are shorthand for some boiler plate code.

Semicolons at the end of a statement are optional and there are several syntactic shortcuts designed to avoid entire classes of errors such as null pointer exceptions.

Kotlin is statically typed, not dynamically typed but most of the time, the compiler is able to infer the data types when the code is compiled so it is not necessary to explicitly declare the type.

Side note – the word “transpiled” appears in the course several times. Note that this means taking source code written in one language which is then transformed into another language with a similar level of abstraction.

How Kotlin/JVM Compiles to Bytecode

Just like Java, Kotlin compiles to bytecode and this is executed by the JVM and the JVM does not care what the original source code was.

To take an example, we might have an application that has a class called main. This is defined in a class called Main.java and it compiles to a file called Main.class which can be run with the command java Main. Of course, this runs, not in isolation but with the classes and interfaces in the Java Runtime Environment (JRE).

When an application is distributed, you must rely on the assumption that the end user has access to the JRE. This means that you only need to distribute the bytecode.

Kotlin works in a similar way with each class compiling to a single bytecode file, but in Kotlin, the starting application for a command line application does not look like a class. Instead, it’s a top-level code file without a class declaration but it must contain a main function that accepts an array of strings as its one and only parameter.

The name of the function in Kotlin might be Main.kt. It is not compulsory to call it this or to have it start with an upper case letter but these are both conventional in Kotlin. When it is compiled to bytecode it is converted into a class because the JVM needs this and so a file called Main.kt is compiled to MainKt.class.

The executable file has dependencies bit you can’t assume that these will be installed on the users computer. Typically when you run MainKt.class, you will also have a fairly lengthy class path which includes the Kotlin standard library jar file and a couple of other supporting jar files.

This means that your Kotlin application includes dependencies from both the Kotlin standard library and from the Java runtime core libraries.

Unlike Java, Kotlin allows you to declare multiple classes in a single code file but each is compiled to its own separate bytecode file. For example, you might have a file which includes a main function and a data class called person. This will compile to MainKt.class and Person.class.

The bottom line here is that the classes compile to bytecode and the JVM doesn’t see a difference between classes compiled from Kotlin source code and classes compiled from Java source code. However, if you are delivering a simple command line application, you will need to deliver both you class file and all of its dependencies.

If you are working in a framework such as Android Studio which already has the Kotlin standard library, you only need to worry about your own code files.

My understanding here, although it is not explicitly stated here, is that what this is getting at is that when you build your application in Android Studio, it will generate an executable that includes any required dependencies.

Kotlin Coding Conventions and Vocabulary

Kotlin mostly follows the same file and identifier conventions as Java. There are some differences.

	•	Packages.  In Kotlin, package names should be in lower case with no underscores but if necessary, will use camel-case.
	•	Package and directory names must match in Java but not in Kotlin.  However, IntelliJ expects there to be the same so it is a good idea to ensure they do.

Java uses reverse domain notation to name packages so example.com becomes com.example followed by a sub package so you end up with a directory structure matching this. That is, a folder called com under the source code root and a sub-directory within that called example and within that a subdirectory called Kotlin and your Kotlin and Java files would be either in this folder or in a subdirectory of this folder.

For an application built in Kotlin only, you don’t need such a deep directory structure. It is recommended that you place your main code file in the source code root directory without creating a package for it. This avoids the need to declare the package in the main code file.

Sub-packages should be created as shallow names so, for instance, a model class used to manage data in memory might go in a package called model. This would have a simple name and does not need the reverse domain notation.

Note that in some environments, the package naming structure is provided. If you're building Android apps for example, your base package matches your application ID. If you have a sub-package here called model, but this is a sub-package of the package matching the application ID.

Function and variable names follow the same conventions as Java, names start with a lower case letter and, if necessary, use camel-case notation. Class and interface names should start with an upper case letter.

In Kotlin, constants are known as compile time constants and are placed in a special place in the code file or the class. An identifier for a constant should be all upper case and individual words should be separated with underscores.

It is also placed inside a special code block called a companion object which actually compiles to a separate bytecode file. A constant is also declared with the keywords const and val so a typical constant declaration might look like this:

	class Constants {
	companion object {
			const val A_CONSTANT = 42
	}
	}

One potential error you might come across is:

	Error:(4, 5) Kotlin: Modifier 'const' is not applicable to 'local variable'

You might see this error if you declare a constant outside of a companion object.

You might also this pair of errors:

	Error:(17, 1) Kotlin: Expecting a top level declaration
	Error:(12, 9) Kotlin: Const 'val' are only allowed on top level or in objects

Not only must you put your constant inside a companion object but the companion object must be inside a class. In this case, the declaration is inside a class but it is not inside a companion object within that class. I guess that this means that a constant must be inside a companion object so within a class, anything declared directly, that is not inside another object such as a companion object, is top level (top level within the class). Since a const declaration must be inside a companion object which gives you a kind of simple hierarchical structure:

	Class > companion object > constant

Kotlin Keywords

Like Java, Kotlin has many keywords which cannot be used as identifiers for any other purpose. However, Kotlin categorizes keywords as either hard or soft.

The hard keywords have meaning everywhere in Kotlin and it is these that cannot be used as identifiers. Examples of these include Class, val, var etc.

With soft keywords, they have a meaning within a specific context and these include things like try and catch which are used in try-catch blocks but outside of the context in which they have a specific meaning in Kotlin, they can be used as identifiers.

Kotlin also has some modifier keywords which are applied to hard keywords. An example of this is companion which can be applied to the hard keyword object and therefore has a very specific meaning in this context. Otherwise, it can also be used as an identifier.

A complete list of these hard and soft keywords and modifier keywords can be found here.