Kotlin is the best(*)

home

 

Kotlin is the new official language of Android devices, sort of. Java was the old one, and Kotlin compiles into Java, so it's really just Java. Kotlin also compiles into javascript so it's absolutely the best for everyone wanting to use the same language for both Android and HTML.

It feels like it was designed in a dark closet, feeling around for parts. It adds Python-like features, but in the most awkward way possible. Standardized syntax is changed for no reason. It has shortcuts no one ever wanted. The parts don't seem to go together. The only good thing I can say about it is it's slightly better than javascript. Or, it would be it browsers ran Kotlin, which they don't.

Inferred semi-colons

Kotlin borrows the rule from Scala that you don't need a semi-colon if the compiler can guess. You don't need them to end a line, but can still split commands over 2 lines. You can sometimes even put several commands on 1 line without semicolons. It's so annoying:

var n=0  // no need for a semi-colon (but it's legal)
n+=6; n=4  // semicolon needed between these

var f : MutableList<Double> =  // continued next line is fine
  mutableListOf(1.0, 2.0)

// no semi-colon needed to end the 1st print
// (since the "else" cuts if off anyway):
if(n>=0) print("A") else print("b")

Just because you can do something doens't mean you should.

Variables, declarations, casts

Basic Types

Kotlin uses the alternate declare syntax: var n:Int = 6. As usual, the type is optional if it can be guessed: var w = "cow" is fine. Somewhat like Swift, var declares a normal variable while val declares a constant, as in: val maxCats=12.

Unlike scripting languages, it has a "heavy" set of types. Along with Int we get Long, Short, and Byte. Decimal values get Float and Double. Besides String we also get Char. Note that types start with caps. Ex's:

var n=0  // implicitly an Int
var n2 : Int = 3  // optionally add colon and the type

// var is normal, val means a constant:
val sun : String = "The Sun"  // sun is a constant

var ch : Char = 'e'  // single-quotes for char
var s2 = sun+ch+7  // + casts most things to a string

var n = 9/4.0  // fine, 9 to 9.0, result is a Double

One suprise is that chars+/-ints give chars: 'a'+5 is 'f' and 'f'-3 is 'c'.

Explicit casts use a member function toType(). Even basic types have them:

3.toDouble()  // 3.0
Math.sqrt(7.0).toInt()  // 2

w = 'a'.toString()  // char converts itself into a string
(4>2).toString()  // "true" bools cast to string!!

Sadly, bools won't cast to ints: true.toInt() is an error. Worse than that and a huge pain: Int's and Float's don't implicitly cast to Doubles. var d:Double=5 is a darn type-mismatch error. You must use 5.0. That's so annoying.

Lists

The array type is MutableList. Apparently Kotlin was made during the "mutable" craze. Use MutableList<Int> and so on. Confusingly, Lists are displayed using square-brackets: [1,5,9] but not coded that way. Create them with mutableListOf(i1, i2, i3):

var L : MutableList<Double> = mutableListOf(5.6, 7.1, 5.0)

L[0]  // 5.6 -- indexing is normal
L.size // really??, Not length??

L.add(50.0)  // to end (of course)
L.removeAt(0)  // answer is 5.6, which is removed
L.remove(7.1)  // searches for that item

Apparently Kotlin once had tuples as their immutable list type, like everyone else. Now it uses List (the same as ImmutableList, except constant). If also gets Pair and Triple classes:

// these are immutable:
var L4 : List<int> = listOf(1,2,3)
var W = listOf("cat", "dog")  // implicitly List<String>
L4[0]=9  // ERROR -- L4 can't be changed
 
var p1 : Pair<Int,String> = Pair(2,"bird")
var p2 = Pair(2,3)  // implicitly Pair<Int,Int>

// accessed using .first and .second:
var b1=p1.first
p2.second=5  // ERR -- immutable

The constructor doesn't have a nice way of making an empty list. Use listOf(). But we get a cute constructor taking a "make 1 item" function:

var L1 = mutableListOf<Int>()  // empty list

// 1st input is the size:
var L2 = List<Int>(3, {i->(i+1)*10})  // 10,20,30
// {i->(i+1)*10 } is a simple anonymous function

Pair and Triple seem to be the prefered types, even though size-2 and 3 lists seems like the same thing with less hassle. They each have a standard list cast: Triple(1,3,5).toList().

There's no slicing, as in L[2..6], but Kotlin has ranges and has: L.slice(2..5). It also allows list add and subtract: L+listOf(5,5,5) adds three 5's to the end. L=L-listOf(0,10) removes all 0's and 10's.

Contains is done with x in L. Instead of notting the whole thing, Kotlin uses !in:

if(4 in listOf(2,3,5,6)) ...
var hasCow = "cow" in W
var noCow = "cow" !in W  // read as: cow not in W

in is pretty much the only thing Kotlin didn't screw up.

There's also a primative array type. It's the same as List, except can't be resized and is mutable. There are shortcuts for an IntArray and BooleanArray and such:

var A : Array<Double> = arrayOf(1.4, 3.0, 9.0)
A.size  // 3
A[0]+=2.0  // arrays are mutable

var B : BooleanArray = booleanArrayOf(true, true, false, true)

The hash-table class uses the C++/Java name Map. The syntax is predictable, then awkward:

var M = mapOf("kitty" to "cat", "puppy" to "dog")
M["kitty"]  // cat
M["pony"] = "horse"

// with explicit type (no surprises):
var NumWords : Map<Int,String> =
  mapTo(1 to "one", 2 to "two", 3 to "three")

That's right -- someone thought "kitty" to "cat" was a nice-looking way to associate keys with values.

Msps also have in and !in. But you may not need them since looking for an illegal value returns null: M["kid"] is always safe.

multi-assign

Multi-assign from a list, pair, or array is allowed, but only in a declaration:

var (a,b)=Pair(5,9)  // a=5, b=9

var(n0,n1)=L  // n0=L[0]  n1=L[1]

There's no rule that the sizes need to match -- the input can be longer. The extra items are ignored. The actual name of this trick in Kotlin is a destructuring declaration.

if's, loops

if statements are the usual:

if(n1!=n2) {}  // a normal if
else {}

But they can also return a value, like the common ?: :

// value-returning if:
s = if(n>0) { +1 } else { -1 }

// We can have statements before the answer. Yow:
w = if(n!=8) { print("A"); "good" }
    else { "bad" }

while (and do-while, bleh) loops work the normal way. Like Python, there's no regular for-loop, and for is really a for-each:

// basic list loop:
for(n in L) print(n)

// optionally supply the type:
for(w:String in listOf("ant","beaver","condor")) ...

// each char in a string:
for(ch in "abcd") print(ch+",")  // a,b,c,d,

You can also use ranges. 1..5 is a Range object, which can be iterated over. (1..5).toList() is a list from 1 to 5. But they only go forwards: 5..1 has 0 elements. To go backwards we need 5 downTo 1:

for(x in 1..10)  // runs 10 times, 1 to 10
// NOTE: the ending # is IN the range

for(i in 0..L.size-1) // index loop

// ugg: this goes backwards:
for(y in 10 downTo 1)

Intervals other than 1 are given with the word step (something I haven't seen since Pascal, for good reason):

for(x:Int in 10..100 step 5)  // 10 15 20 ... 100
for(y in 100 downTo 10 step 5)  // 100 95 ... 15 10

In a burst of silliness, you can use until to not include the ending item (similar to Pythons 1...<n):

for(n in 1 until 10)  // stops on 9

// used as a range in a substring:
var commaPos=w.indexOf(',')
var beforeComma = w.slice(0 until commaPos)  // slice takes a range

Kotlin allows you to fake Python's index+value loop. L.withIndex() creates an iterator. Items are a special index/value IndexedValue object (not a Pair? Whatever):

// old-style:
for(i in 0 until L.length) {
  val v = L[i]

// Python-style:
for((i,v) in L.withIndex())  // i is index, v is value

Kotlin renames switch to when. It works like a normal flexible switch, but also with in:

// a Kotlin switch statement. Ug:
when(n) {
  1 -> "single"
  2,3 -> "several"
  in 4..8 -> "many"
  !in 9..1000 -> "unknown"
  else -> "defualt string"
}

That's using the value-returning version. You can also give it normal commands.

functions

Functions start with keyword fun. Declaring parameters is the usual way, followed by the return-type at the end:

fun addOne(n:Int):Int { return n+1 }

Unit is the word for void. That's a semi-common trick, making "nothing" into a proper type. Ex:

fun show(w:String, times:Int):Unit {
  var res=""
  for(i in 1..times) res+=w;
  print(res)
}

Technically this returns a value of type Unit. var n=show("abc",5) is legal (but pointless).

After losing a bet, they added a value-only body, using = result:

// needless short function syntax:
fun max3(x:Int, y:Int, z:Int) = Math.max(Math.max(x,y),z)

fun sign(n:Double) =
  if(n==0.0) 0.0 else if(n>0.0) +1.0 else -1.0

The return value for these is optional if it can be deduced (this is not the case for normal functions. Or maybe you can pay extra for it. Who knows).

Function pointers

Declaring a function variable is mostly obvious: var f1:(Int,Int)->Int. Assigning it or creating an anonymous function is where things get funny. The easy way to write an anonymous function is like a normal function without the name:

// function pointer declaration. Not too bad:
var f1:(Int,Int)->Int =
  // anonymous function easy version #1:
  fun(a:Int, b:Int):Int { return a+b }

f1(3,7)  // 10

Getting sillier, here's the alternate "just give the return value" syntax:

f1 = { a,b -> a+b }
f1(5,6)  // 11

Those curly-braces are part of it. They have to surround the whole thing. Oddly, the inputs can't have parens around them. Here's another (using a value-returning if, for fun):

f1 = { a,b -> if(a>0) b+10 else b*10 }

Finally, a truely insane rule. To aim a function pointer at a regular function, you need a :: in front:

// a normal function for us to point at later:
fun avg(x:Int, y:Int):Int { return (x+y)/2 }

f1 = avg  // ERROR
f1 = ::avg  // good lord, this is correct

For a nice change, functions taking functions as input work in the usual way. This one takes isWant as an Int->Boolean function to select items from a list:

// a function taking another function as input:
fun select(L:List<Int>, isWant:(Int)->Boolean):MutableList<Int> {
  var Res = mutableListOf<Int>()
  for(n in L)
    if(isWanted(n)) Res.add(n)
  return Res
}

For fun, let's call it with input functions in each of the 3 ways:

var L=listOf(5,6,7,8,9,10,11)

// short-function syntax to select even numbers:
select(L, { n -> n%2==0 } )  // 6,8,10

// normal anon-function systax to get mults of 3:
select(L, fun(a:Int):Boolean { return a%3==0 })  // 6,9

// with a pre-made function to get perfect squares:
fun isSquare(n:Int):Boolean {
  for(i in 1..n/2) if(i*i==n) return true
  return false
}

select(L, ::isSquare)  // pass a function w/double-colons

Nullable types

Kotlin was part of a contest to make the weirdest rules for nullable types. It did very well. They start off fine: type? defines a nullable:

var c1:Cat = null  // error, not nullable
var c1:Cat? = null

var n:Int? = null
n=6

var n1:Int = n // error -- can't cast Int? to Int

So far, so good. Next, Kotlin uses ? as the "abort without crashing on null" operator in the usual way. This will attempt to check c1.claws.length (assuming Cat and claws were declared as nullable):

if(c1?.claws?.length<5) ...

Then Kotlin goes nuts. The "unless the first thing is null" operator changes from ?? which everyone uses to ?: :

// start with a normal and a nullable String:
var w:String? = "corn"  // nullable
var w2:String = "oats"  // non-nullable

// copy w into w2, or "none" if w is null:
w2 = w ?: "none"

A !! after a nullable forces it to be normal (or throws an error if it's null):

// w is nullable, w2 isn't:
w2 = w!!  // compiles, but throws an error if w is null

Then it goes from weird to very weird. When you sucessfully test for non-null, the type changes into the non-nullable version. For example, after the IF below, n changes from Int? to Int:

fun maybeAdd(n:Int?):Int {
  if(n==null) return 0
  // n has now changed from Int? to Int
  return n+1
}

Without that special rule we'd need to use either return n!!+1 if we know n can't be null, or return (n ?: 0)+1 if we suspect it might and want to treat that as a 0.

classes

Kotlin classes don't actually have member variables -- everything is a property with a hidden backing field. But they look like normal fields:

class Cat {
  var name:String=""
  var claws:Int=8
}

var c1=Cat()
c1.claws+=2; // claws is 10

You can add public, private and protected. But the default is public.

Constructors start with ... I can hardy write this it's so silly ... the word constructor, but are otherwise normal:

class Cat {
  var name:String=""
  var claws:Int=8

  constructor(w:String) { name=w }
}

Oddly, that style is called a secondary constructor. The "primary" constructor is this weird but cute 2-part thing. Just after the class name, list parameters in parens. That does nothing by itself. but you can use them to initialize the variables. Here w is assigned in the constructor and initializes name:

// "primary" constructor:
class Cat(w:String) {  // a constructor taking string w,
  var name:String = w  // using w from the constructor above

The real point of a primary constructor is an all-in-one shortcut. Adding var in front declares it as a field, and auto-creates a real constructor:

// establishes "name" as a field
// also provides a constructor which assigns it:
class Cat(var name:String="") {
  var claws:Int=3  // a normal field, for constrast
}

c1=Cat("fritz")  // c1.name is fritz
c1.name += "y"  // name is still a field

If you have both types (I can't imagine why, but it's probably common) secondary contructors are required to call the primary. It uses the old C++-style:

class Cat(var name:String) { // primary

  constructor(w:String, cl:Int) : this(w) {  // calling with w
    claws = if(c1>=0) c1 else 0  // now doing it's own work
  }
// end of Cat

var c1=Cat("eddie")  // calling the main constructor
var c2-Cat("beth",4) // secondary, which calls the primary
var c1=Cat()  // ERR. Currently no constructor like this

Kotlin also took a stab at the contest for class rules no-one wanted. You're allowed to write init's inside of a class, which run when it's created. This uses inits to adjust the name:

class Cat(var name:String)  // all-in-1 constructor for name
  init { print("primary constructor has begun") }
  init {
    if(name=="flully") name="fluffy"
    print("we can have more than one init. Why?")
  }

  // a fun constructor: Cat(4) names the cat "#4":
  constructor(n:Int) : this("#" + n.toString()) {
    print("I happen after primary constructor and all inits")
  }

I suppose init's are so you can use the primary constructor short-cut, but can also add some checking. But it seems it's easier to use a normal declare with a normal constructor.

Adding properties isn't that bad. Fields are already "auto-properties". Just add a get and/or set after them. set takes a name you choose (instead of the implicit value everyone else uses). The hidden backing field is named field:

// adding get/set after a "field":
class Cat {
  var name:String=""  // this was already an auto-property
    // optionally supply our own set/get:
    get() { return field }
    set(w) { if(w!="Hidden Cat X") field=w }

Of course, you can make normal properties which use whatever they want for their backing and don't have extra magic:

class Cat {
  var kilos:Double=0.0  // <-arrg! the .0 is required
  var pounds:Double  // no init since no magic backing 
    get() { return kilos*2.2 }
    set(n) { kilos=n/2.2 }

c1.kilos=10.0
c1.pounds  // 22.0, calling the get
c1.pounds=60  // calling the set. kilos is ... ummm ... 25?

The rule is that if Kotlin sees you using field, or if you don't write get/set at all, you get a secret backing field. In other words, it tries to make one of it sees you need it, otherwise if won't.

Member function actually work normally:

  // Cat member function:
  fun nLen():Int { return name.length }

Subclassing is also mostly normal. Virtual functions need to be explicitly marked with open (the Kotlin made-up word for virtual) and with override down below:

// super-class. Needs "open":
class Animal {
  open eats(n:Int):Int { ...
  // open means virtual
}

// subclass, needs "override":
class Cat : Animal {  // ahhh...normal-looking inheritance
  override eats(n:Int):Int {
    val baseEat=super.eats(days) // prefix "super" to use parent
    ...
  }

Kotlin interfaces are the same as in Java: use interface, and they can't have any content (but they are allowed to write bodies for functions).

The same whacky "checking is casting" trick works with subtypes. Test for a subclass with is. Any is the base class of all classes, so you can have that. If true, the var becomes that type:

fun animalStuff(a:Animal) {
  if(a is Cat) {
    // a is now a Cat variable, this is legal:
    print(a.claws)
  }
  ...
}

The longer way is allowed in Kotlin with as? -- the nullable version of as (which you will never need):

val c1 : Cat? = a as? Cat?
if(c1!=null) print(c1.claws)

Note that we're still using a Kotlin trick here: c1 turns into a non-nullable Cat after if(c1!=null), making c1.claws legal (instead of c1.?claws).

Magic contexts

Kotlin provides 2 way of aliasing a variable: a normal alias, or counting as "inside" of it. The normal alias uses either it, or a name you choose. Exs:
// all this does is set "it" equal to n inside the {}'s:
n.let { if(it>10 || it<1) print("out of range") }

// more useful:
L[0].size.let {  // "it" is a copy of L[0].size
  if(it==4) ...
  if(it>12) ...
}

To choose a name besides it write one before a -> (which is very confusing -- it looks like a function, but it's not):

L[i].let { n->  // n is alias for L[i]
  var x=n*2
  if(n<0) ...
}

I don't see the point of any of these. n=L[i] does the same thing and is easier to read.

The other version of this is an implied class variable. Inside you can call members as if you were in a member function:

w.run {
  if(length>=2) {  // runs w.length
    var start = slice(1..2)  // runs w.slice
  ...
}

//  a time-saver, sort of, for c1.name= and c1.age= :
c1.run { name="alice"; claws=5 }

There are two more of these which do the same things but return different values.

let and run return what's in the the curly-braces. also and apply return the calling variable.

It's silly, but they like to use also with return statements:

// returns n ("also" is like "let", except for the return):
return n.also { print("returning "+it) }

apply is fun for chaining object mutations together. Here each one modifies L and then returns L for the next to work on. Here were chain 2 of them:

L.apply {
  add("cow")
  add("duck")
  // the result of the this is L
}.apply { // this operates on L (with the previous changes):
  remove("lizard")
}
There's one more oddball, so there are 5 of these, total. with duplicates run but with the oblect as an input: with(c1) { } is the same as c1.run { }.

These provide a valuable service. I long suspected this sort of shortcut was stupid. Now, after playing around with them, I know.

File-structure and Java

Kotlin expects to see a top-level fun main(), optionally with an Array<String> parameter. Each file is expected to have package myPackage at the top, which other files include. Ex. of a main file and a Cat class:

// === hello.kt
import Cat.Cat

fun main() : Unit {
  println("global var n="+n)

  var c1 = Cat()
  c1.age=35
  c1.nm="toby"
  println("name="+c1.nm+" age="+c1.age)
}

var n:Int=6  // globals are allowed

// === Cat.kt
package Cat

public class Cat {
  var age : Int = 0
  public var nm : String = "arf"
}

We'd compile all of this using the kotlinc command, but note it goes into a JAR file:

kotlinc hello.kt Cat.kt -include-runtime -d hello.jar

The result, hello.jar runs like the regular old Java file it is:

java -jar hello.jar

 

 

Comments. or email adminATtaxesforcatses.com