The nosey programmer’s guide to Kotlin and Dart
Kotlin and Dart are the new kids of the block that have actually been around for years. Recently they have come into the mainstream as the languages of choice for Android and Flutter development respectively.
This article is for the ‘nosey programmer’ — those of us who are curious enough to want to know the basics, but not ready for a deep dive yet.
Specifically, this article is for Kotlin folk who are curious about Dart, and Dart developers who are wondering why Kotlin is causing a buzz.
If you are feeling nosey, then you can dip into the following Kotlin/Dart topics below.
- Variables
- Typing (Static vs Strong)
- Collections
- Classes & OOP
- Miscellaneous Features
Side Note — embedding Gists into Medium articles was severely broken when I wrote this. If this gets resolved I might update this article to use Gists for code examples.
Quick History
Kotlin was unveiled by JetBrains in 2011. It was started out of a frustration with existing languages targeting the JVM and the sense that there could be something better. Design of the Kotlin language was lead by Andrey Breslav and the language became open sourced in 2012.
Version 1.0 was release in 2016. It is the language used to write JetBrain’s IDEs and since 2017 it has been embraced language of choice by Google for the Android platform. The language is now steered by the Kotlin Foundation which is largely a joint initiative by JetBrains and Google.
Dart was created by Lars Bak and Kasper Lund. First publicly announced in 2011 it reached 1.0 in 2013. The original goal was to create a more structured language for writing web applications. From it’s early days Dart could run in a VM or be compiled to JavaScript. Unfortunately the Dart VM never made its way into the Chrome browser as had been hoped.
Inside Google Dart has been used in large-scale production apps for several years already via Angular Dart, including the web UI for Ad-Words, Google’s main money-making product. Recently Flutter has brought Dart more into the limelight.
Variables
Kotlin and Dart both support variables with inferred and explicit types, final and (compile time) constants.
// KOTLIN
var stringOne = "String One"
var stringTwo : String = "String Two"
val stringThree = "String Three"
const val stringFour = "String Four"//DART
var stringOne = "String One";
String stringTwo = "String Two";
final stringThree = "String Three";
const stringFour = "String Four";
Kotlin and Dart are pretty similar when declaring variables. var
and val
in Kotlin is a nice way of doing the more ‘traditional’ var
and final
in Dart. const val
is a bit more verbose in Kotlin compared to just const
in Dart.
The semicolon!
If you are going to write Dart code you just have to accept that you are going to be hitting the semicolon key a lot. In Dart you need to add a semicolon at the end of each expression.
Kotlin by convention is written without a semicolon. Yay! If you feel compelled, you can use it too.
Statically vs Strongly Typed
Kotlin is statically typed. Dart is strongly typed. What does this mean in principal and in practice?
Statically typed languages are constrained to only support a known set of types. You cannot fake, hide, ignore or disable checking of these types.
Dart is described as being strongly typed. There is less consensus about what a strongly typed language really means (http://bit.ly/2xivqIU). Let’s not get side-tracked on this debate . The key point here is that with a strongly typed language you can circumvent the type system if you want. (In Dart this is via the dynamic
type.)
What does this mean in practice. Well, probably not too much in that both languages will help avoid type errors at compile and run type.
One quick example can illustrate that in both Kotlin and Dart you can’t coerce a variable to accept different types.
//KOTLINvar aStringVar = "A String"
aStringVar = 3 //compile errorvar anAnyVar : Any = "An Any"
anAnyVar = 3 //no error - Any is a super-type//DARTvar aStringVar = "A String";
aStringVar = 3; //compile errorObject anObjVar = "An Object";
anObjVar = 3; //no error - Object is a super-type
We only touched on this topic and there is probably enough for an entire article. Let’s move on.
Collections
With collections we start to see how Kotlin is a more feature-rich language. But let’s not feel sorry for Dart — it has very good collection classes and capabilities.
Basics
Both Kotlin and Dart have Lists, Sets and Maps.
Kotlin however goes further by offering read-only and mutable variations of these. List
and MutableList
, Set
and MutableSet
x, Map
and MutableMap
.
Dart collections are mutable. (Collections declared with const
are immutable, but these are compile time constants so not equivalent to Kotlin’s immutable types.)
Let’s look at some code to see some of basic collection declarations.
//KOTLIN
val fixedNumbersList : List<Int> = listOf(1, 2, 3)
val numbersList : MutableList<Int> = mutableListOf(1, 2, 3)
numbersList.add(4)val fixedNumbersSet : Set<Int> = setOf(1, 2, 3)
val numbersSet : MutableSet<Int> = mutableSetOf(1, 2, 3)
numbersSet.add(4)
val fixedNumbersMap : Map<Int, String> = mapOf(1 to "one", 2 to "two")
val numbersMap : MutableMap<Int, String> = mutableMapOf(1 to "one", 2 to "two")
numbersMap.put(3, "three")//DARTfinal List<int> numbersList = [1, 2, 3];
numbersList.add(4);
final Set<int> numbersSet = {1, 2, 3};
numbersSet.add(4);final Map<int, String> numbersMap = {1: "one", 2: "two"};
numbersMap[3] = "three";
Both Kotlin and Dart have typed collections. These can either be explicitly given (as above) or they will be inferred.
Kotlin and Dart also supports specific collection implementations, such as LinkedList or TreeSet.
Whereas the Kotlin syntax is a little more verbose, Dart provides convenient literal syntax for lists, sets and maps.
Collection Methods
Collections in both Kotlin and Dart offer methods for applying functions over collections. Here are some examples to show some of these capabilities in the two languages.
Filtering
//KOTLINval words = "The quick brown fox jumps over the lazy dog".split(" ")// filter, partition, any, all, none
val longWords = words.filter { it.length > 3 }
//[quick, brown, jumps, over, lazy]
val partitionedWords = words.partition { it.length > 3 }
// ([quick, brown, jumps, over, lazy], [The, fox, the, dog])
words.any { it.length > 3 } //true
words.all { it.length > 2 } //true
words.none { it.length == 1 } // true...//DARTfinal words = "The quick brown fox jumps over the lazy dog".split(" ");
// where, retainWhere, firstWhere, lastWherefinal longWords = words.where((word) => word.length > 3);
//(quick, brown, jumps, over, lazy)final longerWords = longWords.toList()..retainWhere((word) => word.length > 4);
//[quick, brown, jumps]final firstLongWord = words.firstWhere((word) => word.length > 3);
//quickfinal lastLongWord = words.lastWhere((word) => word.length > 3);
//lazy
Predicate based filtering is pretty much the same aside the use of filter
vs where
. Kotlin’s predicate
seems more useful that Dart’s retainWhere
and the testing predicates (any, none, all) would also be nice additions to Dart.
Transformations
//KOTLINval words = "The quick brown fox jumps over the lazy dog".split(" ")// map, zip, associate, flattenval wordLengths = words.map { it.length }
//[3, 5, 5, 3, 5, 4, 3, 4, 3]
val germanWords = "Der schnelle braune Fuchs springt über den faulen Hund".split(" ")val matchedWords = words zip germanWords
//[(The, Der), (quick, schnelle), (brown, braune), (fox, Fuchs),
//(jumps, springt), (over, über), (the, den), (lazy, faulen), (dog, Hund)]val wordLengthAssociation = words.associate { it to it.length }
//{The=3, quick=5, brown=5, fox=3, jumps=5, over=4, the=3, lazy=4, dog=3}
val wordsFlattened = listOf(words, germanWords).flatten()
//[The, quick, brown, fox, jumps, over, the, lazy, dog,
//Der, schnelle, braune, Fuchs, springt, über, den, faulen, Hund]// DARTfinal words = "The quick brown fox jumps over the lazy dog".split(" ");// mapfinal wordLengths = words.map((word) => word.length);
//(3, 5, 5, 3, 5, 4, 3, 4, 3)
Compared to Kotlin, Dart’s collection transformations are limited. zip
, associate
and flatten
in Kotlin are useful and would be great to see in Dart.
Aggregating
//KOTLINval wordLengths = words.map { it.length }
//[3, 5, 5, 3, 5, 4, 3, 4, 3]// reduce, fold, sum, max, min, average
val totalLetterCount = wordLengths.reduce { sum, wordLength -> sum + wordLength }
//35
val totalLetterCountFold = words.fold(0) { sum, word -> sum + word.length }
//35
val totalLetterCountSum = wordLengths.sum()
//35
val longestWordLength = wordLengths.max()
//5
val shortestWordLength = wordLengths.min()
//3
val averageWordLength = wordLengths.average()
//3.888888888888889//DARTfinal wordLengths = words.map((word) => word.length);
//(3, 5, 5, 3, 5, 4, 3, 4, 3)// reduce, foldfinal totalLetterCount = wordLengths.reduce((sum, wordLength) => sum + wordLength);
//35final totalLetterCountFold = words.fold(0, (sum, word) => sum + word.length);
//35
Again, Kotlin provides more aggregation collection methods, although notably reduce
and fold
are also found in Dart.
Dart’s Mutating Methods
It’s worth noting that some collection methods in Dart actually change the collection rather than returning a new one. Specifically these are retainWhere
, sort
and shuffle
. This is a shame and not friendly for functional programming. However, as in the example above creating copy of a collection is quite easy and with Dart’s cascade operator (see below) the code can be kept concise.
Language specific collection features
Kotlin has a collection type called a Sequence which can be used for efficiently executing multiple processing steps on larger collections.
Also in Kotlin is the nice ability to use plus and minus operators to add or remove elements from a collection.
Recently added to Dart as of 2.3 is the handy spread operator (…) which makes adding multiple elements to a list a one-liner.
Also worth checking out is this contribution from Pascal Welsch which brings some of Kotlin’s collection features (like immutable collections) to Dart.
Classes and OOP
Classes and object oriented programming is part of both languages. As with collections, Kotlin offers more features and capabilities. Let’s look a some code to get a feel for both languages.
//KOTLINfun main() {
val job:InterruptableJob = InterruptableJob("100")
job.log() //Job(100 :: Status: IDLE)
runJob(job)
job.log() //Job(100 :: Status: RUNNING)
pauseJob(job)
job.log() //Job(100 :: Status: PAUSED)
}fun runJob(job:Job) { job.run() }
fun pauseJob(job:InterruptableJob) { job.pause() }interface Job {
val id:String
fun run()
}enum class JobStatus {
IDLE, RUNNING, PAUSED, CANCELLED, FAILED, COMPLETE
}abstract class BaseJob(override val id: String) : Job {
var jobStatus = JobStatus.IDLE
protected set
fun isIdle(): Boolean = jobStatus == JobStatus.IDLE
fun isRunning(): Boolean = jobStatus == JobStatus.RUNNING
fun isFailed() : Boolean = jobStatus == JobStatus.FAILED
fun isComplete(): Boolean = jobStatus == JobStatus.COMPLETE
override fun run() { jobStatus = JobStatus.RUNNING }
fun log() { println("Job(${id} :: Status: ${jobStatus})") }
}class InterruptableJob(id: String) : BaseJob(id) {
internal fun pause() { jobStatus = JobStatus.PAUSED }
internal fun cancel() { jobStatus = JobStatus.CANCELLED }
override fun run() { super.run() /*do something*/}
}
Above shows a few core OO concepts. Kotlin supports interfaces and abstract classes and inheritance. Classes can have overloaded constructors and can pass values or call methods of a super class.
The code also makes use of some visibility modifiers (e.g. internal, protected) and an enum class.
There are lots of OO features not seen here, some of which we will touch on below.
Let’s look at an implementation in Dart.
//DARTvoid main() {
final job = InterruptableJob("100");
job.log(); //Job(100) :: Status: JobStatus.IDLE
runJob(job);
job.log(); //Job(100) :: Status: JobStatus.RUNNING
pauseJob(job);
job.log(); //Job(100) :: Status: JobStatus.PAUSED
}runJob(Job job) => job.run();
pauseJob(InterruptableJob job) => job.pause();class Job {
String id;
run() {}
}enum JobStatus {
IDLE, RUNNING, PAUSED, CANCELLED, FAILED, COMPLETE
}abstract class BaseJob implements Job {
String id;
JobStatus _jobStatus = JobStatus.IDLE;
BaseJob(this.id);
bool isIdle() => _jobStatus == JobStatus.IDLE;
bool isRunning() => _jobStatus == JobStatus.RUNNING;
bool isComplete() => _jobStatus == JobStatus.COMPLETE;
run() => _jobStatus = JobStatus.RUNNING;
log() => print("Job(${id}) :: Status: ${_jobStatus}");
}class InterruptableJob extends BaseJob {
InterruptableJob(String id) : super(id);
pause() => _jobStatus = JobStatus.PAUSED;
cancel() => _jobStatus = JobStatus.CANCELLED;
run() => super.run();
}
This example illustrates that Dart has some interesting omissions when it comes to OOP.
Dart does not have an interface type. Instead every class implicitly defines an interface. This means you can use the interface of a class (without its implementation) via the implements keyword.
Dart does not support constructor overloading. Instead it is possible to used named constructors. For example we could add to the BaseJob a constructor method like BaseJob.fromJson()
.
Similarly, Dart does not support method overloading.
There are no visibility modifiers in Dart. However declaring a variable as method with a ‘_’ prefix makes it private to a class or namespace.
Whilst Dart clearly offers simplified OO capabilities, it does support key constructs needed for object oriented programming, namely polymorphism, inheritance, encapsulation and class introspection with dynamic invocation (dart:mirrors). (Perhaps method overloading is an obvious omission.)
Language specific features
We only scratched the surface of Kotlin’s OO features. Dive deeper and you will find:
- Extensions : add functionality to a class without having to inherit or decorate.
- Data classes: data classes with automatic implementations of equals(), hash() methods and more.
- Sealed classes: prescribe a limited set of types in a class hierarchy.
- Nested classes, Type aliases, Delegation
Dart also provides the facility to use Mixins as an alternative way to share implementations across classes.
Other language features
It’s impossible to fully cover both languages here, however I wanted to mention a few features that will be seen if writing Kotlin or Dart.
Annotations
Both languages provide support for writing annotations to provide additional meta-data for IDEs and code analysis.
Threading & Asynchronicity
Kotlin can use the JVM’s threading mechanisms either via the thread
method, or by implementing the Runnable
interface and calling start on an instance of Thread
, as with Java.
kotlinx.coroutines are lighter-weight mechanism for aynchronous code that avoids the relatively heavy-weight and error-prone JVM threading mechanics.
Dart does not support shared memory threading. Instead Dart has a construct called an Isolate to take advantage of multi-core CPUs. An Isolate has it’s own memory heap — in this way they are isolated from each other. Communication between Isolates is achieved via message passing instead.
TypeAlias and TypeDef
Both Kotlin and Dart support creating named functions so they can be referenced as Function objects.
Kotlin uses typealias to do this, whilst Dart uses typedef.
Dart Cascades
The cascades notation allows chaining method invocation or property access on an object in a very concise syntax.
//DARTfinal planetBuilder = PlanetBuilder();
planetBuilder
..setName("Earth")
..setMass(5.97)
..setSurfaceType(Surface.Terrestrial)
..setGravity(9.8);
final Planet earth = planetBuilder.build();
Reflection / Introspection
Kotlin supports reflection and also enables use of Java’s reflection mechanisms.
Dart supports reflection, however via a separate package which is currently marked as being unstable. In essence, a big problem with reflection in Dart is that Dart is most often run outside it’s VM, i.e. for Flutter or JS.
Code Organisation, Packages & Imports
Kotlin and Dart take different approaches to organising programs. Let’s look at these by starting with two code examples and then highlighting the differences.
Packages
Kotlin and Dart have very different concepts when it comes to a package.
In Kotlin a package is similar to Java. It’s basically a namespace. All Kotlin code must be in a package. If none is specified the package is ‘default’.
However Kotlin is more flexible in that the package structure does not have to be mirrored by the directory structure. In other words you can have all Kotlin files in one directory but still organise the code in multiple different packages.
In Dart the term package is used to describe a library or module. It is a way to distributed and share functionality. There is no package keyword in the language.
The directory structure for a Dart programme is significant.
Imports
In Kotlin you can use code from other namespaces via imports. Code in the same namespace can be referenced without an import. Importing using the package name is the same for code from external libraries as it is for code in the same program. This will be very familiar to Java or .NET developers.
In Dart you can import features from other files, other packages or the core language. This means you will see three variations of import in Dart:
- Relative imports — these import private code in the same package. For example:
import 'src/utils.dart';
- Package imports — these imports are prefixed with “package:” and import features from an external package. For example:
import 'package:http/http.dart';
- Language imports — importing core language features are prefixed with “dart:”. For example:
import 'dart:math';
Notice how relative and package imports reference a .dart
file whereas language imports do not. Namespaces in Dart are based on the a file path.
Read More: https://dart.dev/guides/packages
Tooling and Runtimes
Tools
Kotlin has fantastic support inside Intellij-based tools — which is to be expected. The IDE actively suggests improvements and optimisations to the Kotlin code you write and can convert Java to Kotlin.
Maven and Gradle plugins link Kotlin into build tools for JVM-based applications. Command-line compilation is available and support for Eclipse is still on offer.
There are a few Dart tools available. The Dart/Flutter teams provide plugins for IntelliJ and Visual Studio Code. These both provide very good Dart syntax checking and refactoring support. Tight integration with Flutter tools are also baked in.
A bunch of command-line tools are also on offer, including for code formatting, static analysis and documentation generation.
Runtimes
Kotlin primarily targets the JVM and is compiled into JVM bytecode. As such, it can be used in JVM-friendly environments (Android, servers etc).
However Kotlin is breaking free of the JVM with the Kotlin Multiplatform project. The approach is to allow Kotlin to have extensions that allow platform-specific implementations. This is in contrast to code generation.
Dart was originally conceived to run in a VM, and that option still exists today. Indeed you can still write server-side Dart. More typical, however, is for Dart to be compiled into JavaScript or ARM instructions for Flutter.
TL;DR
Kotlin and Dart have risen in popularity over the last couple of years, propelled by their use for mobile app development (Android and Flutter respectively).
Both languages support features you would expect to see today; functional and OO programming constructs, strict/strong typing and type inference, generics, annotations. All this is accompanied by high quality tooling.
This article takes a quick look at both languages. There is a lot that is not covered.
Hopefully this will help Kotlin developers understand that Dart is not just an attempt to fix JavaScript. Likewise Dart developers can get an insight into what the buzz around Kotlin is about.