Java without Java - Kotlin

Content:

  1. Introduction
  2. val vs var
  3. null handling
  4. Built in types
  5. Operations
  6. Control structures
  7. Object Oriented Programming
  8. Generic Programming
  9. Functional Programming
  10. Kotlin and Java
  11. Runtime
  12. DSL features
  13. Why switch to Kotlin

Introduction:

Kotlin is a language invented by JetBrains in 2011.

Kotlin is used as major language by some newer companies (like AWS, NetFlix and Uber) and are widely used for Android apps (and Google is officially supporting it for that).

Kotlin is in many ways an "improved Java" or "Java how it would look like if it was designed today".

Some of Kotlin's syntax is taken from C#.

Kotlin can target:

  1. JVM
  2. JavaScript
  3. native via LLVM

The entire article assume that the reader will be looking at using Kotlin for JVM platform.

The entire article assumes that the reader is proficient in the Java language and everything is explained by comparing it to Java.

Some C# syntax will also be shown. Both to show that Kotlin got some syntax from C# and to help developers that are more familar with C# than Java.

This is not a complete reference to Kotlin. This only covers some of the more important features. For more details see the Kotlin Reference or get a Kotlin book.

val vs var:

Variable declarations are different in Kotlin:

Kotlin Java equivalent C# equivalent
val v = 123 final int v = 123; N/A
val v: Int = 123 final int v = 123; N/A
var v = 123 int v = 123; var v = 123;
var v: Int = 123 int v = 123; int v = 123;

Example:

package jwj

fun main(args: Array<String>) {
    val iv1 = 123
    //iv1 = 456
    var iv2 = 456
    println("$iv1 $iv2")
    iv2 = 789
    println("$iv1 $iv2")
    val iv3: Int = 123
    //iv3 = 456
    var iv4: Int = 456
    println("$iv3 $iv4")
    iv4 = 789
    println("$iv3 $iv4")
}

null handling:

Types xxx in Kotin are not nullable. Types xxx? in Kotlin are nullable.

Kotlin Java equivalent C# equivalent
var v: Int int v; int v;
var v: Int? Integer v; int? v;
var o: MyClass N/A N/A
var o: MyClass? MyClass o; MyClass o;
res = o.m() res = o.m(); res = o.m();
res = o?.m() if(o != null) res = o.m(); else res = null;
or:
res = (o != null) ? o.m() : null;
res = o?.m()

Example:

package test

fun main(args: Array<String>) {
    //val iv1: Int = null;
    val iv2: Int? = null
    println("${iv2 == null}")
    //val sv1: String = null;
    val sv2: String? = null
    println("${sv2 == null}")
    val sv3: String? = sv2?.substring(0, 1);
    println("${sv3 == null}")
}

Built in types:

Kotlin has the same basic types as Java plus unsigned integers.

Kotlin Java equivalent C# equivalent
Byte byte sbyte
Byte? java.lang.Byte sbyte?
Short short short
Short? java.lang.Short short?
Int int int
Int? java.lang.Integer int?
Long long long
Long? java.lang.Long long?
Float float float
Float? java.lang.Float float?
Double double double
Double? java.lang.Double double?
Char char char
Char? java.lang.Character char?
Boolean boolean bool
Boolean? java.lang.Boolean bool?
String N/A N/A
String? java.lang.String string
Unit void or Void void
UByte N/A byte
UShort N/A ushort
UInt N/A uint
ULong N/A ulong

Note that unsigned types in Kotlin are experimental.

Arrays are classes in Kotlin.

Kotlin Java equivalent C# equivalent
Array<MyClass> MyClass[] MyClass[]
ShortArray short[] short[]
IntArray int[] int[]
LongArray long[] long[]
FloatArray float[] float[]
DoubleArray double[] double[]

Example:

package jwj

@kotlin.ExperimentalUnsignedTypes // avoid warnings for new unsigned types
fun main(args: Array<String>) {
    val vb: Byte = 123
    println("$vb")
    val vs: Short = 123
    println("$vs")
    val vi: Int = 123
    println("$vi")
    val vl: Long = 123L
    println("$vl")
    val vf: Float = 123.456f
    println("$vf")
    val vd: Double = 123.456
    println("$vd")
    val vc: Char = 'A'
    println("$vc")
    val v: Boolean = true;
    println("$v")
    val vs1: String = "ABC"
    println("$vs1")
    val vs2: String = """ABC"""
    println("$vs2")
    // new unsigned types:
    val vub: UByte = 123U
    println("$vub")
    val vus: UShort = 123U
    println("$vus")
    val vui: UInt = 123U
    println("$vui")
    val vul: ULong = 123UL
    println("$vul")
}

Operations:

Kotlin has the normal arithmetic operators: +, -, *, / and %.

Kotlin has the normal comparison operators: ==, !=, <, <=, > and >=.

Note that == is same value and Kotlin has a separator operator === for same identity.

Kotlin Java equivalent C# equivalent
a == b a.equals(b) a.Equals(b)
or:
a == b
a === b a == b Object.ReferenceEquals(a, b)

Kotlin has the normal boolean operators: &&, || and !.

Kotlin use method calls for type conversion.

Kotlin Java equivalent C# equivalent
v.toByte() (byte)v (sbyte)v
v.toShort() (short)v (short)v
v.toInt() (int)v (int)v
v.toLong() (long)v (long)v
v.toFloat() (float)v (float)v
v.toDouble() (double)v (double)v

Note that in Kotlin explicit conversion is required for both larger->smaller and smaller->larger conversion unlike Java where it is only needed for larger->smaller.

Kotlin'a bitwise operators are different from Java.

Kotlin Java equivalent C# equivalent
shl << <<
shr >> >>
ushr >>> N/A
and & &
or | |
xor ^ ^
inv ~ ~

Example:

package jwj

fun main(args: Array<String>) {
    val v1 = 123
    val v2 = ((4 * v1 + 5) / 6 - 7) % 8
    println("$v2")
    var bv: Byte = 123
    var iv: Int = 123
    //bv = iv
    bv = iv.toByte()
    //iv = bv
    iv = bv.toInt()
    val b1 = 0x0000001
    val b2 = b1 shl 31
    val b3 = b1 xor b2
    val b4 = b3.inv()
    println("$b4")
    val iv1 = 123
    val iv2 = 123
    println("${iv1 == iv2}")
    println("${iv1 === iv2}")
    val a = 'A'
    val b = 'B'
    val c = 'C'
    val sv1 = "$a$b$c"
    val sv2 = "$a$b$c"
    println("${sv1 == sv2}")
    println("${sv1 === sv2}")
}

Control structures:

Kotlin has very powerful control structures.

Kotlin Java equivalent C# equivalent
if(a) {
    foo()
} else {
    bar()
}
if(a) {
    foo();
} else {
    bar();
}
if(a)
{
    foo();
}
else
{
    bar();
}
res = if(a) b else c
res = a ? b : c;
res = a ? b : c;
when(x) {
    1 -> { foo() }
    2 -> { bar() }
    else -> { throw new Exception("Ooops") }
}
switch(x) {
    case 1:
        foo();
        break;
    case 2:
        bar();
        break;
    default:
        throw new Exception("Ooops");
}
switch(x)
{
    case 1:
        foo();
        break;
    case 2:
        bar();
        break;
    default:
        throw new Exception("Ooops");
}
when(o) {
    in 1..10 -> { println("in 1..10") }
    is Int -> { println("Int not in 1..10") }
    else -> { println("not Int") }
}
if(o instanceof Integer) {
   int io = (Integer)o;
   if(1 <= io && io <= 10) {
       System.out.println("in 1..10");
   } else {
       System.out.println("Int not in 1..10");
   }
} else {
   System.out.println("not Int");
}
if(o is int)
{
   int io = (int)o;
   if(1 <= io && io <= 10)
   {
       Console.WriteLine("in 1..10");
   }
   else
   {
       Console.WriteLine("Int not in 1..10");
   }
}
else
{
   Console.WriteLine("not Int");
}
res = when(o) {
    is Int -> "Int"
    is String -> "String"
    else -> "Other type"
}
if(o instanceof Integer) {
    res = "Int";
} else if(o instanceof String) {
    res = "String";
} else {
    res = "Other type";
}
if(o is int)
{
    res = "Int";
}
else if(o is string)
{
    res = "String";
}
else
{
    res = "Other type";
}
for(i in ia) {
    ...
}
for(int i : ia) {
    ...
}
foreach(int i in ia)
{
    ...
}
for(i in 1..10) {
    ...
}
for(int i = 1; i <= 10; i++) {
    ...
}
for(int i = 1; i <= 10; i++)
{
    ...
}
while(c) {
    ...
}
while(c) {
    ...
}
while(c)
{
    ...
}
do {
    ...
} while(c)
do {
    ...
} while(c)
do
{
    ...
} while(c)

Kotlin supports both default values for arguments and call by argument name.

Kotlin Java equivalent C# equivalent
fun m(v: Int = 0) {
    ...
}
void m() {
    m(0);
}
void m(int v) {
    ...
}
void m(int v = 0)
{
    ...
}
m(v = 1)
N/A
m(v: 1);

Example:

package jwj

fun m(a: Int = 123, b: Int = 456): Unit {
    println("$a $b")
}

fun main(args: Array<String>) {
    val v = 7
    val s1 = if(v == 0) "zero" else "not zero"
    println(s1)
    val s2 = when(v) {
        0 -> "zero"
        else -> "not zero"
    }
    println(s2)
    val a = arrayOf(1, 2, 3)
    for(i in a) {
        println(i)
    }
    for(i in a.indices) {
        println(i)
    }
    for(i in 1..3) {
        println(i)
    }
    for(i in 3 downTo 1) {
        println(i)
    }
    m(321, 654)
    m(321)
    m(b = 654)
    m()
}

Object Oriented Programming:

Both classes and methods are by default final in Kotlin unlike Java.

Properties in Kotlin are simply declared with val and var to indicate only getter or both getter and setter. If needed then explicit getter and setter can be specified.

One constructor is declared automatically by the class declaration.

A class can be declared as a data class to automatically get suitable equals, hashcode, tostring and copy methods added.

As a result the Kotlin code is often significant shorter than the equivalent Java code.

Visibility can be controlled via public, internal, protected and private modifiers. Public, protected and private has same meaning as in Java. Internal means visibile within same compilation. Default is public

Kotlin Java equivalent C# equivalent
class X {
   ...
}
open class Y {
   ...
}
final class X {
   ...
}
class Y {
   ...
}
sealed class X
{
   ...
}
class Y
{
   ...
}
class X {
    ...
    fun m1() { ... }
    open fun m2() { ... }
    ...
}
class X {
    ...
    final void m1() { ... }
    void m2() { ... }
    ...
}
class X
{
    ...
    void m1() { ... }
    virtual void m2() { ... }
    ...
}
class X {
    val a: Int = 123
    var b: Int
    ...
}
class X {
    private int a = 123;
    public int getA() {
        return a;
    }
    private int b;
    public int getB() {
        return b;
    }
    public void setB(int b) {
        this.b = b;
    }
    ...
}
class X
{
    public int A { get; } = 123
    public int B { get; set; }
    ...
}
class X {
    val a: Int = 123
        get() {
            ...
            return field
        }
    var b: Int
        get() {
            ...
            return field
        }
        set(value) {
            ...
            field = value
        } 
    ...
}
class X {
    private int a = 123;
    public int getA() {
        ...
        return a;
    }
    private int b;
    public int getB() {
        ...
        return b;
    }
    public void setB(int b) {
        ...
        this.b = b;
    }
    ...
}
class X
{
    private int a = 123;
    public int A
    {
        get
        {
            ...
            return a;
        }
    }
    private int b;
    public int B
    {
        get
        {
            ...
            return b;
        }
        set
        {
            ...
            b = value;
        }
    }
    ...
}
class X(val v: Int) {
    ...
}
class Y(var v: Int) {
    ...
}
class Z(v: Int) : W(v) {
    ...
}
class X {
    private int v;
    public X(int v) {
        this.v = v;
    }
    public int getV() {
        return v;
    }
    ...
}
class Y {
    private int v;
    public Y(int v) {
        this.v = v;
    }
    public int getV() {
        return v;
    }
    public void setV(int v) {
        this.v = v;
    }
    ...
}
class Z extends W {
    public Z(int v) {
        super(v):
    }
    ...
}
class X
{
    private int v; 
    public int V { get { return v; } }
    public X(ony v)
    {
        this.v = v;
    }
    ...
}

class Y
{
    public int V { get; set; }
    public Y(int v)
    {
        V = v;
    }
    ...
}

class Z : W
{
    public Z(int v) : base(v)
    {
    }
    ...
}
data class X(val a: Int, val b: Int)
class X {
    private int a;
    private int b;
    public X(int a, int b) {
        this.a = a;
        this.b = b;
    }
    public int getA() {
        return a;
    }
    public int getB() {
        return b;
    }
    @Override
    public boolean equals(Object o) {
        // check for equality based on a and b
    }
    @Override
    public int hashCode() {
        // calculate hash code based on a and b
    }
    @Override
    public String toString() {
        // construct String based on class name, a and b
    }
    // copy method is not easily implementable in Java
}
class X
{
    public int A { get; set; }
    public int B { get; set; }
    public X(int a, int b)
    {
        A = a;
        B = b;
    }
    public override bool Equals(Object o)
    {
        // check for equality based on a and b
    }
    public override int HashCode() {
        // calculate hash code based on a and b
    }
    public override string ToString()
    {
        // construct String based on class name, a and b
    }
    // copy method is not easily implementable in C#
}

Abstract classes and interfaces in Kotlin are just like in Java.

Enums in Kotlin are just like in Java.

Kotlin Java equivalent C# equivalent
abstract class AC {
    ...
    abstract fun m()
    ...
}
class CC : AC {
    ...
    override fun m() {
        ...
    }
    ...
}
abstract class AC {
    ...
    abstract void m();
    ...
}
class CC extends AC {
    ...
    @Override
    void m() {
        ...
    }
    ...
}
abstract class AC
{
    ...
    abstract void m();
    ...
}
class CC : AC
{
    ...
    override void m()
    {
        ...
    }
    ...
}
interface I {
    ...
    fun m()
    ...
}
class C : I {
    ...
    override fun m() {
        ...
    }
    ...
}
interface I {
    ...
    void m()
    ...
}
class C implements I {
    ...
    @Override
    void m() {
        ...
    }
    ...
}
interface I
{
    ...
    void m()
    ...
}
class C : I
{
    ...
    void m()
    {
        ...
    }
    ...
}
enum class E { A, B, C }
enum E { A, B, C }
enum E { A, B, C }

Example:

package jwj

open class P(val iv: Int) {
    open fun dump() {
        println("$iv")
    }
}

class C(iv: Int, val sv: String) : P(iv) {
    override fun dump() {
        super.dump()
        println("$sv")
    }
}

data class Data(val iv: Int, val sv: String)

fun main(args: Array<String>) {
    val o = C(123, "ABC")
    o.dump()
    val d1 = Data(123, "ABC")
    val d2 = Data(123, "ABC")
    println("$d1 $d2 ${d1==d2}")
    val d3 = d1.copy()
    val d4 = d1.copy(iv=456)
    println("$d3 $d4 ${d3==d4}")
}

Kotlin does not have static instead it has objects and companion objects.

An object is an all static class or a singleton.

A companion object represent what in Java would be static part of a class.

Kotlin Java equivalent C# equivalent
object X {
    var v = 0
    fun m() { ... }
}
...
tmp = X.v
X.m()
class X {
    private static int v;
    public static int getV() {
        return v;
    }
    public static void setV(int v) {
        X.v = v;
    }
    public void m() { ... }
}
...
tmp = X.getV();
X.m();
static class X
{
    public static int V { get; set; }
    static void M() { ... }
}
...
tmp = X.V;
X.M();
class X {
    companion object XO {
        fun mo() { ... }
    }
    fun m() { ... }
}
...
o = X()
o.m()
X.mo()
class X {
    static void mo() { ... }
    void m() { ... }
}
...
o = new X();
o.m();
X.mo();
class X
{
    static void Mo() { ... }
    void M() { ... }
}
...
o = new X();
o.M();
X.Mo();

Example:

package jwj

object O {
    fun m() {
        println("O.m")
    }
}

class SC {
    companion object SCO {
        var compf = 0
    }
    var instf : Int
    init {
        compf++
        instf = compf
    }
    override fun toString() : String {
        return "$instf of $compf"
    }
}

fun main(args: Array<String>) {
    O.m()
    val o1 = SC()
    println(o1)
    val o2 = SC()
    println(o1)
    println(o2)
}

Kotlin got a very nice feature to make delegation easier.

Kotlin supports operator overload.

Kotlin supports extension methods.

Kotlin Java equivalent C# equivalent
interface I {
    // method definitions
    // ...
    // ...
    // ...
}
class C : I {
    // implementation of all methods
    // ...
    // ...
    // ...
}
class CX(val real: I) : I by real {
    // few overrides that do extra work
    // and delegate to real object
}
interface I {
    // method definitions
    // ...
    // ...
    // ...
}
class C implements I {
    // implementation of all methods
    // ...
    // ...
    // ...
}
class CX(I real) implements I {
    private I real;
    public CX(I real) {
        this.real = real;
    }
    // implementation of all methods
    // that for a few do extra work
    // and for all delegate to real object
    // ...
    // ...
    // ...
}
interface I
{
    // method definitions
    // ...
    // ...
    // ...
}
class C : I
{
    // implementation of all methods
    // ...
    // ...
    // ...
}
class CX(I real) : I
{
    private I real;
    public CX(I real)
    {
        this.real = real;
    }
    // implementation of all methods
    // that for a few do extra work
    // and for all delegate to real object
    // ...
    // ...
    // ...
}
class X {
    ...
    operator fun plus(o: X) = ...
    operator fun minus(o: X) = ...
    operator fun times(o: X) = ...
    operator fun div(o: X) = ...
    operator fun get(ix: Int): E = ...
    operator fun set(ix: Int, o: E) { ... }
}
N/A
class X
{
    ...
    static X operator+(X a, X b) { ... }
    static X operator-(X a, X b) { ... }
    static X operator*(X a, X b) { ... }
    static X operator/(X a, X b) { ... }
    E this[int] 
    {
        get { return ...; }
        set { ... value ...; }
    }
}
fun SomeType.newmethod(arg: ArgTyp): RetType {
    // do whatever with this (SomeType) and arg (ArgTyp)
}
N/A
static class Extensions
{
    RetType newmethod(this SomeType me, ArgTyp arg)
    {
        // do whatever with me (SomeType) and arg (ArgTyp)
    }
}

Example:

package jwj

interface I {
    fun m1()
    fun m2()
}

class C1 : I {
    override fun m1() { println("C1.m2") }
    override fun m2() { println("C1.m2") }
}

class C2(val real: I) : I by real {
    override fun m2() {
        println("Voila")
        real.m2()
    }
}

data class MyInt(val v: Int) {
    operator fun plus(o: MyInt) = MyInt(v + o.v)
    operator fun minus(o: MyInt) = MyInt(v - o.v)
    operator fun times(o: MyInt) = MyInt(v * o.v)
    operator fun div(o: MyInt) = MyInt(v / o.v)
}

class TenInt {
    val a = arrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
    operator fun get(ix: Int) = a[ix]
    operator fun set(ix: Int, v: Int) { a[ix] = v }
}

fun Int.plusPowerOf2(n: Int): Int {
    return this + (1 shl n)
}

fun main(args: Array<String>) {
    val ox = C2(C1())
    ox.m1()
    ox.m2()
    val i1 = MyInt(1)
    val i2 = MyInt(2)
    val i3 = MyInt(3)
    val i7 = i3 * i2 + i1
    println(i7)
    val ten = TenInt()
    println(ten[7])
    ten[7] = 17
    println(ten[7])
    println(1.plusPowerOf2(0))
    println(1.plusPowerOf2(1))
    println(1.plusPowerOf2(2))
    println(1.plusPowerOf2(3))
}

Generic Programming:

Generic programming in Kotlin is similar to generic programming in Java - the syntax is just sligtly different.

Kotlin Java equivalent C# equivalent
class C<T> {
    ...
}
class C<T> {
    ...
}
class C<T>
{
    ...
}
class C<T : MyClass> {
    ...
}
class C<T extends MyClass> {
    ...
}
class C<T> where T : MyClass
{
    ...
}
interface I<in T> {
    // methods that take a T as input
}
...
fun m(o: I) {
    ...
}
...
m(instanceOfClassImplementingIforTorSuperClass)
interface I<T> {
    // methods that take a T as input
}
...
void m(I<? super T> o) {
    ...
}
...
m(instanceOfClassImplementingIforTorSuperClass);
interface I<in T>
{
    // methods that take a T as input
}
...
void m(I<T> o)
{
    ...
}
...
m(instanceOfClassImplementingIforTorSuperClass);
interface I<out T> {
    // methods that produce a T as output
}
...
fun m(o: I) {
    ...
}
...
m(instanceOfClassImplementingIforTorSubClass)
interface I<T> {
    // methods that produce a T as output
}
...
void m(I<? extends T> o) {
    ...
}
...
m(instanceOfClassImplementingIforTorSubClass);
interface I<out T>
{
    // methods that produce a T as output
}
...
void m(I<T> o)
{
    ...
}
...
m(instanceOfClassImplementingIforTorSubClass);

Example:

package jwj

class G1<T>(val o: T)

open class GP {
    override fun toString() = "GP"
}

class GC : GP() {
    override fun toString() = "GC"
}

class G2<T : GP>(val o: T)

interface I3I<in T> {
    fun mi(o: T?)
}

interface I3O<out T>
{
    fun mo(): T?
}

class G3I<T>: I3I<T> {
    override fun mi(o: T?) { }
}

class G3O<T> : I3O<T> {
    override fun mo(): T? = null
}

fun <T> f3i(o:I3I<T>) { }

fun <T> f3o(o:I3O<T>) { }

fun main(args: Array<String>) {
    val o1int = G1<Int>(123)
    println(o1int.o)
    val o1infint = G1(123)
    println(o1infint.o)
    val o1string = G1<String>("ABC")
    println(o1string.o)
    val o1infstring = G1("ABC")
    println(o1infstring.o)
    //val dummy = G2(123)
    //val dummy = G2("ABC")
    val o2p = G2(GP())
    println(o2p.o)
    val o2c = G2(GC())
    println(o2c.o)
    f3i<GP>(G3I<GP>())
    //f3i<GP>(G3I<GC>())
    f3i<GC>(G3I<GP>())
    f3i<GC>(G3I<GC>())
    f3o<GP>(G3O<GP>())
    f3o<GP>(G3O<GC>())
    //f3o<GC>(G3O<GP>())
    f3o<GC>(G3O<GC>())
}

Functional Programming:

Kotlin supports functions as first class objects and lambda's.

Kotlin Java equivalent C# equivalent
(Type1, Type2) -> ReturnType
@FunctionalInterface
interface SomeInterface {
    ReturnType someMethod(Type1 arg1, Type2 arg);
}
delegate ReturnType name(Type1 arg1, Type2 arg2)
{ arg1: Type1, arg2: Type2 -> ... }
(Type1 arg1, Type2 arg2) -> ...
(Type1 arg1, Type2 arg2) => ...
{ arg1, arg2 -> ... }
(arg1, arg2) -> ...
(arg1, arg2) => ...

Example:

package jwj

val eps = 0.0000000001

fun diff(f: (Double) -> Double, x: Double): Double {
    return (f(x + eps) - f(x)) / eps
}

fun newton_solve(f: (Double) -> Double): Double {
    var x = 1.0
    var xold:Double
    do {
        xold = x
        x = x - f(x) / diff(f, x)
    } while(x/xold < (1 - eps) || x/xold > (1 + eps))
    return x
}

fun main(args: Array<String>) {
    val sqrt_func = { x: Double -> x * x - 2 }
    val sqrt2 = newton_solve(sqrt_func)
    println("$sqrt2 ${sqrt2  * sqrt2}")
    val curt3 = newton_solve({ x: Double -> x * x * x - 3 })
    println("$curt3 ${curt3 * curt3 * curt3}")
    val fortg_func = { x: Double, a: Double -> x * x * x * x - a }
    val fort4_func = { x: Double -> fortg_func(x, 4.0) } 
    val fort4 = newton_solve(fort4_func)
    println("$fort4 ${fort4 * fort4 * fort4 * fort4}")
}

Kotlin and Java:

The basics:

Kotlin and Java classes with methods and property accessor/mutator are fully interoperable.

Example:

package jwj;

public class JavaClass {
    private int v;
    public int getV() {
        return v;
    }
    public void setV(int v) {
        this.v = v;
    }
    public void m() {
        System.out.printf("v=%d\n", v);
    }
    public void callback() {
        KotlinClass okt = new KotlinClass(0);
        okt.setV(123);
        okt.m();
    }
}
package jwj

class KotlinClass(var v: Int) {
    fun m() { println("v=$v") }
}
 
fun main(args: Array<String>) {
    val oj = JavaClass()
    oj.v = 123
    oj.m()
    oj.callback()
}

Some special cases:

Calling from Kotlin to Java is in general very simple. But calling from Java to Kotlin have a few special cases due to Kotlins more compact code.

A Kotlin property becomes one field and one getter and if mutable also one setter. So if one put an annotation on a Kotlin property then one may want some control over where the annotation is actually put.

There are special get, set and field prefixes that can be used to specify where a property annotation is put.

Kotlin does not have static methods. But one may need to provide a static method for Java to call.

There is a @JvmStatic annotation to specify that a method should be a Java static method.

Kotlin can have default values for method arguments, but Java does not. One may want to call such a method from Java.

There is a @JvmOverloads annotation to specify that Kotlin should generate overloads for Java to call.

Kotlin does not have fields like Java. But one may need to expose a field for Java to use.

There is a @JvmField annotation to specify a property to be a Java field.

Example:

package jwj;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class JavaAnn {
    public void check(C1 c1) {
        try {
            Field f = C1.class.getDeclaredField("p");
            System.out.println(f.getAnnotation(A1.class) != null);
            System.out.println(f.getAnnotation(A2.class) != null);
            System.out.println(f.getAnnotation(A3.class) != null);
            Method mg = C1.class.getDeclaredMethod("getP", new Class<?>[0]);
            System.out.println(mg.getAnnotation(A1.class) != null);
            System.out.println(mg.getAnnotation(A2.class) != null);
            System.out.println(mg.getAnnotation(A3.class) != null);
            Method ms = C1.class.getDeclaredMethod("setP", new Class<?>[] { String.class });
            System.out.println(ms.getAnnotation(A1.class) != null);
            System.out.println(ms.getAnnotation(A2.class) != null);
            System.out.println(ms.getAnnotation(A3.class) != null);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
    public void test(C2 c2) {
        c2.p = c2.p + "X";
        c2.m();
        c2.m(0);
        c2.m(0, 0);
        O.m();
    }
}
package jwj

annotation class A1

annotation class A2

annotation class A3

class C1 {
    @get:A1
    @set:A2
    @field:A3
    var p = "ABC"
}

class C2 {
    @JvmField
    var p = "ABC"
    @JvmOverloads
    fun m(a: Int = 123, b: Int = 456) {
        println("C1.m $a $b")
    }
}

object O {
    @JvmStatic
    fun m() {
        println("O.m")
    }
}

fun main(args: Array<String>) {
    val ja = JavaAnn()
    val c1 = C1()
    ja.check(c1)
    val c2 = C2()
    ja.test(c2)
}

Runtime:

Kotlin fundamentally uses the Java runtime, but Kotlin has added some extra Kotlin specific classes.

This is an overview of some of the more important ones.

Package kotlin:

Any
Base class of all classes
XxxArray
Classes for simple arrays
Array<T>
Class for object arrays
Nothing
Uninstantiable class
Pair<T1,T2>
Pair of values

See also basic types here.

Package kotlin.collections:

Kotlin has its own set of collections. They are very similar to Java's collection but provide better support for immutable collections.

The hierarchys are:

List<out E>
    MutableList<E>
        ArrayList<E>
Map<K, out V>
    MutableMap<K, V>
        HashMap<K, V>
Set<out E>
    MutableSet<E>
        HashSet<E>

There are conversion methods between arrays and collections:

Example:

package jwj

fun main(args: Array<String>) {
    val a = arrayOf(1, 2, 3)
    println(a.indices)
    val lst1 = listOf(1, 2, 3)
    println(lst1)
    println(lst1.indices)
    //lst1.add(4)
    //lst1[1] = 0
    val lst2 = mutableListOf(1, 2, 3)
    println(lst2)
    println(lst2.indices)
    lst2.add(4)
    lst2[1] = 0
    println(lst2)
    val map1 = mapOf(Pair(1, "A"), Pair(2, "BB"), Pair(3, "CCC"))
    println(map1)
    println(map1.keys)
    println(map1.values)
    //map1.add(Pair(4, "DDDD"))
    //map1[2] = "X"
    val map2 = mutableMapOf(Pair(1, "A"), Pair(2, "BB"), Pair(3, "CCC"))
    println(map2)
    println(map2.keys)
    println(map2.values)
    map2.put(4, "DDDD")
    map2[2] = "X"
    println(map2)
    val set1 = setOf(1, 2, 3)
    println(set1)
    //set1.add(4)
    val set2 = mutableSetOf(1, 2, 3)
    println(set2)
    set2.add(4)
    println(set2)
}

Besides the usual methods for collections that Java has had since forever then Kotlin also got a number of methods for lambda usage.

filter
list/array to list/array with subset of elements
map
list/array to list/array with converted elements
fold
list/array to single value
associate
list/array to map

Example:

package jwj

fun main(args: Array<String>) {
    val lst = mutableListOf(1, 2, 3, 3)
    println(lst)
    println(lst.sum())
    println(lst.count())
    println(lst.average())
    println(lst.any({ v -> v > 2 }))
    println(lst.any({ v -> v > 3 }))
    println(lst.all({ v -> v > 0 }))
    println(lst.all({ v -> v > 1 }))
    println(lst.distinct())
    println(lst.filter({ v -> v > 1}))
    println(lst.fold(0, { acc, v -> acc + v }))
    lst.forEach({ v -> println(" $v") })
    println(lst.map({ v -> v + 1 }))
    println(lst.map({ v -> "Item: $v" }))
    println(lst.associate({ v -> Pair(v, "Item: $v")}))
}

DSL features:

Kotlin got some features for enabling writing DSL (Domain Specific Language) in Kotlin.

Kotlin supports infix notation.

The statement:

a b c

is the same as:

a.b(c)

And when combined with lambda's that can produce some new language syntax.

Languages like Pascal and Visual Basic have a WITH statement.

In Kotlin:

o.a = 123
o.b = "abc"

can be written as:

with(o) {
    a = 123
    b = "abc"
}

And even though it looks like a with statement then it really is an infix method call with a lambda argument.

Example:

package jwj

class CI {
    infix fun moveto(n: Int) {
        println("moveto $n")
    }
}

class CW {
    var a = 0
    var b = ""
}

fun main(args: Array<String>) {
    val o = CI()
    o.moveto(3)
    o moveto 3
    val w = CW()
    with(w) {
        a = 123
        b = "abc"
        println("$a $b")
    }
}

Why switch to Kotlin:

I definitely believe that for some projects it will be beneficial to switch from Java to Kotlin.

Benefits of Kotlin compared to Java (in order of significance):

  1. Save a lot of code for properties
  2. Save a lot of method code for data classes
  3. Save a lot of code for delegating classes
  4. More safe null handling
  5. More readable code via operator overload
  6. Simple function variables
  7. Availability of unsigned data types

Overall I would expect Kotlin code to have 25-50% fewer lines of code than the same Java code.

Drawbacks of Kotlin compared to Java (in order of significance):

Article history:

Version Date Description
1.0 December 22nd 2018 Initial version
1.1 March 24th 2019 Add information about JVM annotation and DSL features

Other articles:

See list of all articles here

Comments:

Please send comments to Arne Vajhøj