Skip to content

1.) Major Syntax Differences

Gabor Varadi edited this page Sep 11, 2021 · 17 revisions

inverted argument order, no semi-colons, void vs Unit, Object vs Any?, fun

The first thing we might notice when we look at Kotlin code is that:

  • we must specify functions with fun

  • we must specify argument list with the name of the argument first, colon, then the type

  • we must specify the return type of the function at the end, also separated with a colon

  • void functions don't need to have anything written there, and implicitly they return Unit

  • we must specify @Override as override (mandatory language keyword)

  • we must use Any? instead of Object

  • there is no new keyword for instantiating classes

So for example, the following code:

package guide.to.kotlin;

public abstract class BaseClass {
    // ...
}

public class MyClass extends BaseClass {
    private final Map<String, Object> objects = new HashMap<>();

    public MyClass() {
        System.out.println("MyClass created");
    } 

    @Override
    protected void addToMap(String key, Object value) {
        objects.put(key, value);
    }

    public <T> T getObject(String key) {
        return (T) objects.get(key);
    }
}

final MyClass myClass = new MyClass();

would now in Kotlin look like this:

package guide.to.kotlin

abstract class BaseClass {
    // ...
}

class MyClass: BaseClass() {
    private val objects = hashMapOf<String, Any?>()

    init {
        println("MyClass created")
    } 

    override fun addToMap(key: String, value: Any?) {
        objects.put(key, value);
    }

    fun <T> getObject(key: String): T = 
        objects.get(key) as T

    // note: we could have used `{ return ... }` here, 
    // but as it is single-line, this is preferred.
}

val myClass = MyClass()

interfaces with val, var and fun

What's important to know in Kotlin is that:

  • var represents a regular mutable variable. Although var should only be used when we actually want to mutate the variable over time, and val should be used where able.

  • val represents a final variable in Java.

  • Each var field exposes a getter/setter, and each val field exposes a getter to Java consumers. In fact, specifying val blah: String get() = "Text" is equivalent to public String getBlah() { return "Text"; }.

  • vars can have different visibility modifiers, for example a public getter, but private setter.

With all that in consideration, an interface like this:

public interface MyContract {
    String getValue();

    void setValue(String value);

    String getName();

    void doSomething(String input1, String input2);
}

looks like this in Kotlin:

interface MyContract {
    var value: String
    
    val name: String
 
    fun doSomething(input1: String, input2: String)
}

And implemented like this

class MyImpl(
    override var value: String,
    override val name: String
): MyContract {
    override fun doSomething(input1: String, input2: String) {
        ...
    }
}

constructors, inheritance

Constructors are fairly special in Kotlin, in the sense that they also contain field declarations, and even passing arguments to super-constructors.

So the following Java code:

public class MyClass extends BaseClass implements MyInterface {
    private String mutableField = "";
    private final String name;

    public MyClass(String tag, String name) {
        super(tag);
        this.name = name;
    }

    public String getMutableField() {
        return mutableField;
    }

    public void setMutableField(String mutableField) {
        this.mutableField = mutableField;
    }

    @Override
    public void methodFromInterface() {
    }
}

is (almost) equivalent to the following in Kotlin:

class MyClass(
    tag: String,
    private val name: String,
    var mutableField: String = "" // default argument
): BaseClass(tag), MyInterface {
    override fun methodFromInterface() {
    }
}

It is worth knowing that it is possible to define multiple constructors using the constructor keyword, which can call other constructors with this() or super(). It is also needed if annotations are added to it (for example, class MyClass @Inject constructor() {}.

class MyCustomView : FrameLayout {
    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(context, attrs, defStyleAttr)

lateinit vars

If you know that a value in Kotlin will be initialized later so you can guarantee that it will be non-null on any future access, instead of defining it as name: String?, you can define it as a lateinit var name: String.

Please note that accessing an uninitialized lateinit variable results in an exception.

It's fairly common to use lateinit var inside Activity/Fragment calls (because you commonly initialize things in onCreate/onViewCreated), so it's best to know about this.

enum class

Compared to just saying

enum Colors {
    RED,
    BLUE
}

Kotlin uses the term enum class instead.

enum class Colors {
    RED,
    BLUE
}

Good news is that enum classes know all the goodies that Java enums do, like constructor arguments, or abstract methods.

annotation class

In Java, you define an annotation with public @interface MyAnnotation {}. In Kotlin, you do this with annotation class MyAnnotation.

final by default vs open

Any class we create is final by default - meaning that it cannot be extended. This is because inheritance is easy to get wrong, so as per "Effective Java", Kotlin tries to enforce this by ensuring that a class can be inherited from only if it is explicitly marked as open.

Therefore,

public final class FinalClass {
}

is

class FinalClass {
}

While the following in Java

public class SomeClass {
}

is the following in Kotlin

open class SomeClass {
}

Note that abstract classes in Kotlin are open by default.

for loops (in ranges, until)

Regular for-loops in Java are actually trickier in Kotlin, as it introduces new keywords. However, we should also note that it's quite common to use .foreach { instead of for(...) { loops (or some other constructs from the Collections API like map or filter that can do the for-if for us instead), but it can be useful to know about for loop quirks nonetheless.

for(int i = 0; i < n; i++) {
    // do something
}

is now:

for(i in 0 until n) {
    // do something
}

where 0 until n is actually a range defined as 0..n-1.

Regular loops with iterators look like this:

for(pair in pairs) {
    ...
}

And pairs can actually be deconstructed (seen later), but this can be applied with maps, for example:

for((key, value) in map.entries) {
    ...
}

object for singleton

In Kotlin, it's possible to very easily (maybe a bit too easily) define a singleton. Meaning any top-level object exposes a .INSTANCE accessor to Java. It looks like this:

object MySingleton {
    // this is a singleton

    fun something() {
    }
}

And accessed from places like this:

MySingleton.something()

Fairly straightforward. Try not to over-use it if possible (as you should normally be wary of singletons).

But it's worth noting that you can use it in the following ways, too (thanks jmfayard):

1.) Have an object where you define your constants:

object Network {
    const val API_URL = "http://httpbin.org"
    const val TIMEOUT = 1000
}

2.) Use it as a namespace for certain functions

// this is possibly a static factory method for Java users
object Positions {
    @JvmStatic
    fun create(x: Int, y: Int) : Int = Position(x, y)
    //....
}

statics via companion object, const val

An important thing to note is that statics "don't exist" in Kotlin.

So while the following in Java is pretty legit:

public class MyClassWithStatics {
    public static final String SOME_CONSTANT = "Hello!";

    public static String someUglyMutableField = "don't do this like ever";

    public static void hello() {
        System.out.println(SOME_CONSTANT + " " + someUglyMutableField);
    }
}

public class MyOtherClass {
    void doSomething() {
        String constant = MyClassWithStatics.SOME_CONSTANT;
        String someUglyMutableField = MyClassWithStatics.someUglyMutableField;
        MyClassWithStatics.hello();
    }
}

Instead, Kotlin introduces the concept of companion objects that store static fields and methods.

Now, this might seem crazy, but it has the super-nice benefits that lets you inherit "static functions" from interfaces into your companion object. This might sound crazy, but this makes sense for example when you need to use the Parceler<T> interface in combination with the @Parcelize annotation using kotlin-android-extensions (to simplify the creation of CREATORs).

It's also important for allowing the creation of extension functions that are accessed as "static methods" of a given class.

So the above code in Kotlin would look like this:

class MyClassWithStatics {
    companion object {
        const val SOME_CONSTANT = "Hello!"

        @JvmField var someUglyMutableField: String = "don't do this like ever"

        @JvmStatic fun hello() { println("$SOME_CONSTANT $someUglyMutableField") }
    }
}
// consuming Java code!
public class MyOtherClass {
    public void doSomething() {
        String constant = MyClassWithStatics.SOME_CONSTANT;
        String someUglyMutableField = MyClassWithStatics.someUglyMutableField;
        MyClassWithStatics.hello();
    }
}

Now you might ask, "wait what are those @JvmField and @JvmStatic annotations for".

If you don't specify those, your Java consumer side (if you have such) would look like this:

public class MyOtherClass {
    public void doSomething() {
        String constant = MyClassWithStatics.Companion.INSTANCE.SOME_CONSTANT;
        String someUglyMutableField = MyClassWithStatics.Companion.INSTANCE.someUglyMutableField;
        MyClassWithStatics.Companion.INSTANCE.hello();
    }
}

Which is honestly hideous, so if you use the auto-converter, beware that it puts this stuff in your code by default instead of the annotations above.

visibility in Kotlin (public by default, private, protected, internal)

In Java, we have the following visibilities:

  • private

  • package (this is the default)

  • protected

  • public

In Kotlin however, we have the following visibilities:

  • private

  • protected

  • public (this is the default)

  • internal

What's important to note is that internal is not the same as package, don't try to use internal as a replacement for package.

There is NO package visibility in Kotlin.

I say this because your auto-converter will add internal everywhere. Just make them public instead.

internal has two odd caveats:

1.) it has a very strange access from Java code unless you use @JvmName

2.) it doesn't really do anything if your code is not multi-module (because internal restricts visibility to only the current compilation module).

So use internal only where it makes sense.

static nested class in Java vs inner class in Kotlin

In Kotlin, all nested classes are equivalent with public static class in Java.

So to make a nested class be an "inner" class, it must be specified as inner class.

class MyAdapter: RecyclerView.Adapter<MyAdapter.ViewHolder>() {
    inner class ViewHolder(view: View): RecyclerView.ViewHolder(view) {
        ...
    }
}

backticks for reserved keywords (Mockito.when, $) and import aliases

It can happen that you are using a library written in Java that uses a reserved keyword in Kotlin (such as object or when or anything that includes a $ symbol).

In this case, Kotlin allows you to use this without problems, as long as it is wrapped in backticks (`).

For example,

Mockito.`when`(text.hashCode())

However, we can help this scenario by using an import alias, and use a different name.

import org.mockito.Mockito.`when` as whenever

whenever(text.hashCode())

is instead of instanceof, as and as? for casting

We might be used to using instanceof and (MyClass) for casting, but Kotlin has a different syntax.

public class Cat {
    private final String name;

    public Cat(String name) {
        this.name = name;
    }

    public void meow() {
        System.out.println("The cat " + name + " meowed.");
    }
}

void doSomething(Object obj) {
    if(obj instanceof Cat) {
        ((Cat)obj).meow();
    }
}

would now look like this in Kotlin:

class Cat(private val name: String) {
    fun meow() {
        println("The cat $name meowed.")
    }
}

fun doSomething(obj: Any?) {
    if(obj is Cat) {
        obj.meow() // note that as `obj` is a `val`, the cast is automatic here
    }
}

If we cannot leverage smart-casting, then we can use as (throws exception on failure) or as? (returns null).

(obj as? Cat)?.meow()

Also worth noting here that smart-casting wouldn't work with mutable nullable field variables, and should be re-assigned to a val first, like val name = name. But there will be an example for this later.

creating anonymous implementations for classes/interfaces

Anonymous interface implementations have a very quirky syntax in Kotlin. Namely you need to create an anonymous object. It's easier to show an example.

myView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        // do something
    }
});

would now be

myView.setOnClickListener(object: View.OnClickListener {
    override fun onClick(view: View) {
        // do something
    }
})

Of course, I can sense pitchforks on the horizon because you can use Java 8 syntax and lambdas. And if you use Kotlin against a Java interface (or a fun interface since Kotlin 1.4.20+), then SAM (single-abstract-method) conversion will let you use a lambda expression in Kotlin as well.

myView.setOnClickListener((view) -> {
    // do something
});

and

myView.setOnClickListener { view ->
    // do something
}

Note that we can ditch the enclosing () around the last lambda argument of a function, this is called "trailing lambdas".

Important Kotlin 1.4 change: Since Kotlin 1.4, you can also use trailing lambdas (and not just object: MyInterface {) with interfaces, if you declare the interface as fun interface. This needs the interface to only have a single method.

there is no condition ? true : false ternary operator

Indeed, in Kotlin there is no ternary operator! It's very frustrating when you first run into it. Then you forget that you ever wanted it.

There are two tricks to know about the ternary operator in Kotlin:

  • you can use val something = if(...) ... else ...

  • you generally shouldn't use if-else anyways in most cases and should generally prefer = when

val something = when {
    value != "hello" -> "beep"
    else -> "boop"
}

The nice thing to note here is that you can combine these keywords into assignments.

To quote the original docs:

In Kotlin, if is an expression, i.e. it returns a value. Therefore there is no ternary operator (condition ? then : else), because ordinary if works fine in this role.

single-line functions can be used with = ... instead of { return ... }

As shown above, you can turn methods like this:

fun getRepository(): Repository {
    return repository
}

into

fun getRepository(): Repository = repository

and in fact, in this particular case, we can ditch fun get and make it a val get():

val repository: Repository get() = repository

If you're using Dagger and you have a bunch of @Provides fun getSomething() { return something } methods, these tricks should generally be applied to them.

arrayOf and arrayOfNulls and listOf and mutableListOf and linkedMapOf

Kotlin provides functions that initialize collections as part of the standard library, and should typically be preferred compared to using their constructors directly.

For example,

private final List<String> strings = new ArrayList<>();

is generally replaced with

private val strings = arrayListOf<String>()

You may note that "hold on, the class used to be seen as a List, but now it's an ArrayList<String>", and you'd be right. In Kotlin, the mutator methods are separated into a separate MutableList interface, so if we do want to mutate this list in place over time, we should use either mutableListOf<T>(), or arrayListOf<T>().

Same goes for mapOf<K, V>(), mutableMapOf<K, V>(), and linkedMapOf<K, V>().

And of course for setOf<T>(), mutableSetOf<T>(), hashSetOf<T>(), I'm sure you get the idea.

Also, you create a list with a size of elements and a lambda for each element. (thanks @jmfayard)

val squares = List(size = 6) { index -> index * index } 
// same as:
val squares = listOf(0, 1, 4, 9, 16, 25)

array literal in annotations (even for single-argument vararg parameter)

In annotations (and in annotations only!), single-argument vararg parameters must be passed as an array.

Previously it used to be with arrayOf(value1, value2) (and single-arg was allowed without arrayOf), but people found this verbose compared to Java's { value1, value2 }.

So Kotlin added support for "array literal in annotations" in 1.2, but now single-arg must also be passed in array.

@Component(modules = [PresentationModule::class], dependencies = [SingletonComponent::class])
interface PresentationComponent {
}

operator conventions (get() vs [], .equals() vs ==)

Many names that a given method had in Java are converted implicitly to "operator"s in Kotlin.

For example, == in Kotlin actually translates to .equals(), and === is referential equality.

Also a notable example is String value = map.get("key") can be written as val value = map["key"] instead.

<, <=, > and >= are mapped to .compareTo calls, and it is possible to override other operators such as +=, -=, +a and so on.

there are no checked exceptions, .use()

Because in Java it was very common to do this:

try {
    ...
} catch(Exception e) {
    throw new RuntimeException(e);
}

Instead, in Kotlin, all exceptions are unchecked. But you can still catch and throw them as you normally would.

try {
    ...
} catch(e: Exception) {
    ...
} finally {
    ...
}

What's important to note is that try(Closeable closeable = new Closeable()) { ... } from Java is actually implemented with a function called use.

Closeable().use { closable ->
    ...
}

volatile is replaced with @Volatile

It might be important to know that volatile is no longer a keyword, but you can use @Volatile to achieve the same behavior.

For synchronized, we can use the synchronized(lock) { ... } function from the Kotlin standard library.

multiple generic bounds

In Java, we can write the following:

public class MyClass<T extends SomeClass & Comparable> {
    ...
}

If we need to specify multiple bounds in Kotlin, we need to use a very different syntax.

class MyClass<T: SomeClass> { // this would be single-bounds

class MyClass<T>
    where T: SomeClass,
          T: Comparable { // multiple generic bounds