Kotlin: General Concepts
Improvements
Kotlin was introduced as a Java ecosystem language variant with the following improvements:
- More concise (less verbose) syntax:
functiontofun.unitreturn Type forvoidMethods.:replaces bothextendsandimplements.newConstructor keyword isn't required.
- Greater Null Safety and Safe Navigation support:
- Elvis operator.
?Nullable Type suffix.nullandNothingType.- Nullable disallowed by default.
- Functional programming paradigm oriented from the beginning:
- Functions can be free-standing and don't have to be attached to or declared within a Class.
- Functions don't have to be Methods.
- Tail Recursion optimization.
- Many performance improvements:
- Asynchronous light-weight Coroutines pre-Java 21.
- Type inferencing.
- Compilation.
- Singleton declaration.
- Greater flexibility with Class and Variable declarations:
val(constant, replacesfinal, immutable) andvar(mutable).- Constructors in Class definition.
publicvisibility by default.statickeyword removed.openmust be explicitly set in Concrete Class definitions to allow Subclassing.- Keyword support for
companion objectandobjectSingletons. lateinitlazy Variable initialization.
Resources and Links
Code samples:
Kotlin: Classes and Types
Types Equality
Unlike Java, Kotlin equality checks mirror JavaScript supporting both "looser" and "stricter" degrees of comparison:
==(Structural equality) compares by Values.===(Referential equality) compares by same Pointer/Memory Address.nullhasNothingType.
Classes
- Constructors are parameterized on Class definitions. (Similar to GoLang
struct.) - Classes aren't subclassable (
final) by default: useopento override (Interfaces and Abstract Classes are). implementsandextendsare both replaced by:.- Interfaces have some features that Abstract Classes do in Java (can define some Getters/Setters). Otherwise, Interfaces define Method signatures (Abstract Methods) and can't be initialized (Abstract Classes can define state and have initialized fields) like usual.
publicis the default visibility.
// By default all Classes can't be inherited from.
// Add the open keyword to allow that.
// Note the constructor in parentheticals
open class ExampleA(a: String, b: String) {
var stringVariable: String = a
val stringValue: String = b
// Method - Function on Class
fun testA(): Unit {
println("stringVariable: $stringVariable, stringValue: $stringValue")
}
}
class ExampleB(a: String, b: String, c: String): ExampleA(a, b) {
var additionalStringVariable: String = c
fun testB(): Unit {
println("stringVariable: $stringVariable, stringValue: $stringValue, additionalStringVariable: $additionalStringVariable")
}
// Inner class
inner class ExampleC(d: String) {
val additionalStringValue: String = d
fun testC(): Unit {
println("stringVariable: $stringVariable, stringValue: $stringValue, additionalStringVariable: $additionalStringVariable, additionalStringValue: $additionalStringValue")
}
}
}
open class ImplicitGettersAndSetters() {
var stringVariable: String = ""
val stringValue: String = "I'm set already as a val"
// Method - Function on Class
fun getFields(): Unit {
println("stringVariable: $stringVariable, stringValue: $stringValue")
}
}
Resources and Links
Code samples:
- https://github.com/Thoughtscript/kotlin_2024/blob/main/kotlin/src/main/kotlin/io/thoughtscript/example/language/Classes.kt
- https://github.com/Thoughtscript/kotlin_2024/blob/main/kotlin/src/main/kotlin/io/thoughtscript/example/language/AbstractClass.kt
- https://github.com/Thoughtscript/kotlin_2024/blob/main/kotlin/src/main/kotlin/io/thoughtscript/example/language/Interfaces.kt
Kotlin: Variables
Variable Declaration
valspecifies an immutable Variable (constant, rather thanfinal).varspecifies a mutable Variable.
Destructing
Kotlin supports Destructuring the fields of an Object into separate Variables:
val (name, age) = person
Lazy Initialization
lateinit allows Variables to be initialized lazily. They must be initialized before they're used:
fun exampleA(): Unit {
// Can't be val
lateinit var name: String // Can't be initialized
name = "I'm initialized" // Placeholder for lengthy or asynchronous assignment
println("I'm initialized with $name")
}
Classes support Receivers, ::, and the .isInitialized field:
class Example() {
lateinit var name: String // Can't be initialized
// Method - Function on Class
fun isInitializedExample(): Unit {
name = "I'm initialized" // Placeholder for lengthy or asynchronous assignment
// Supports :: and .isInitialized in Classes
if (::name.isInitialized) {
println("Initialized value present: $name")
} else {
println("var name is not initialized yet")
}
}
}
Resources and Links
Kotlin: Null Safety
Kotlin supports advanced Null Safety features.
Nullable
By default, Types aren't Nullable unless they're explicitly declared so with a suffixed ?:
// From the Official Documentation
var a: String = "abc" // Regular initialization means non-nullable by default
a = null // compilation error
// From the Official Documentation
var b: String? = "abc" // can be set to null
b = null // ok
Read more here: https://kotlinlang.org/docs/null-safety.html#checking-for-null-in-conditions
Elvis Operator
Kotlin supports the Ruby/Rails Type Safety Navigation:
- Elvis operator
?(akin to Ruby/Rails). .let()(handles non-null),.run()handlesnull
fun chainedSafeElvis(arg: Any?): Unit {
// if null then execute block
arg?.let {
// if not-null then execute this block
// with reference to obj via 'it'
println("chainedSafeElvis: $it")
} ?:run {
println("chainedSafeElvis: Null found")
}
}
Resources and Links
Code samples:
Kotlin: Coroutines
Kotlin supports Asynchronous operations and light-weight Threading.
Suspend Functions
suspend indicates that a Function can be used in either Asynchronous or blocking operations.
They can be called, paused, or stopped.
// function to be launched
// suspend indicates that the Function can be called in async operations, paused, or stopped
suspend fun suspendedFunctionExampleA(text: String): String {
println("I'm in the returned suspended function with $text")
return "I'm done: $text"
}
suspend fun suspendedFunctionExampleB(text: String) = coroutineScope {
println("I'm in the suspended function with no return: $text")
// No return
}
Coroutines
Coroutines, suspend functions, etc. provided support for lightweight virtual threading.
// https://kotlinlang.org/docs/coroutines-overview.html
// Coroutines are Kotlin's approach to Threading and Asynchronous operations
// Akin to Java's Lightweight Virtual Threads
// See also Scope builder notation: https://kotlinlang.org/docs/coroutines-basics.html#scope-builder
fun spawnExecExample() = runBlocking {
println("I'm in the blocking scope")
// Define a Channel like in go
val channel = Channel<String>()
// Declare a coroutine block (like a go routine)
// Spawns a light-weight virtual thread
// is a Job
// Use GlobalScope.launch now
GlobalScope.launch {
println("I'm in a coroutine")
// https://kotlinlang.org/docs/composing-suspending-functions.html#concurrent-using-async
// Conceptually very similar to Launch except it's deferred and can use .await()
val resultA = async { suspendedFunctionExampleA("exampleA") }.await()
println("I'm awaited at resultA: $resultA")
// Concurrent launching - not blocking!
val resultB = async { suspendedFunctionExampleA("exampleB") }.await()
val resultC = async { suspendedFunctionExampleA("exampleC") }.await()
// The documention shows an example where async operations are composed
// but using the .await() is required to print a returned a value like in the below
async { suspendedFunctionExampleB("exampleD") }
// IPC send to channel
async {
for (x in 1..10) channel.send("$x")
channel.close() // close channel
}
// Can compose the above without using promises
println("I'm composed: $resultA, $resultB, $resultC")
}
for (y in channel) println("Channel received: $y")
// Commenting out this line will prevent the blocking scope from printing everything!!
}
Apparently, these are still more performant than (Java 21) Virtual Threads: https://medium.com/@AliBehzadian/java-virtual-threads-performance-vs-kotlin-coroutines-f6b1bf798b16
Resources and Links
Code samples:
Kotlin: Handling Static Methods and Singletons
Static Keyword
- No
staticKeyword.initblock instead ofstaticblocks for complex initializations.
companion objectcan be used to define the equivalent ofstaticMethods.- https://github.com/Thoughtscript/cockroachdb-kotlin-client/blob/master/cockroachdb-kotlin-client/src/main/kotlin/com/cockroachlabs/client/queries/ReadRow.kt
- Only one
companioninstance is present at any given time (object) and allows methods to be called without declaring a new instance of the Class.
Singletons
Singletons can be declared through a single keyword: object:
object Singleton {
fun doSomething() = "Doing something"
}
Companion Objects
companion objectcan be used to define the equivalent ofstaticMethods.- Only one
companioninstance is present at any given time (object) and allows methods to be called without declaring a new instance of the Class.
class ReadRow {
companion object {
@JvmStatic
fun execute(conn: Connection) {
val query = "SELECT * FROM ..."
}
}
}
Resources and Links
- https://kotlinlang.org/docs/classes.html#companion-objects
- https://www.baeldung.com/kotlin/singleton-classes
Code samples: