Yalantis: iOS, Android And Web App Development Company

Kotlin VS Java: Basic Syntax Differences

Kotlin is much younger than Java, but it’s a really promising programming language and its community is constantly growing. Everyone’s talking about it and saying it’s cool. But why is it so special?

We’ve prepared a series of articles to share our experience developing Android apps in Kotlin with you. We’ll discuss how Kotlin differs from Java when it comes to syntax, usability, UI performance and asynchrony so you could decide which language suits you best.

Let’s start with some basic syntax differences. Here’s the first:

1. With Kotlin, you can do more with less code

One of the main advantages of Kotlin is its concision. You get more functionality with less code. And the less code you write, the fewer mistakes you make. It’s pretty straightforward. Let’s look at the basics of Kotlin, starting with classes.

public final class Person {
    private String name;
    private int age;
    private float height;

    public Person(String name, int age, float height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        this.height = 1.8f;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public float getHeight() {
        return height;
    }

    public void setHeight(float height) {
        this.height = height;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", height=" + height +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Person person = (Person) o;

        if (age != person.age) return false;
        if (Float.compare(person.height, height) != 0) return false;
        return name != null ? name.equals(person.name) : person.name == null
    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        result = 31 * result + (height != +0.0f ? Float.floatToIntBits(height) : 0);
        return result;
    }   
}

Above is a usual Java class. It doesn’t do much. It just contains some data. But it’s painful to see how large this snippet of code is when you realize how little it brings to the table. To cheer you up, we’ll show you an equivalent class written in Kotlin.

data class Person(var name: String,
                 var age: Int,
                 var height: Float = 1.8f)

Yes, you’ll automatically get needed getters, setters, equals(), hashcode(), toString() and copy() functions for your data class! Of course, you can easily override these functions, but in most cases it’s enough just to declare the class and its properties.

This is exactly what we meant when we said that Kotlin is concise.

2. You can avoid NullPointerException

Now we want to remind you about the biggest pain in numerous programming languages – the null pointer exception. We can hardly imagine how many developers have suffered from the null pointer ever since Tony Hoare invented it in 1965 while trying to make things a bit simpler.

Sadly, we can’t go back in time and prevent Tony from making this mistake. But with Kotlin, we can now easily escape the NullPointerException.

val person: Person? = null
...
person?.name = "John"

If the variable is nullable, the compiler will not permit you to access it without a proper check. Kotlin forces you to use a ?. operator. This prevents the app from crashing automatically.

How does it work under the hood? Let’s review the generated bytecode.

L2
LINENUMBER 18 L2
ALOAD 3
DUP
IFNULL L3
LDC "John"
INVOKEVIRTUAL igalata/com/kotlinexample/Person.setName (Ljava/lang/String;)V
GOTO L4
L3
POP

As you can see, we’ve got the same null check here. JetBrains’ developers (who created Kotlin) knew that checking our variables every time is the only way to avoid a NullPointerException. But they also knew that Android developers don’t want to deal with the NullPointerException in their projects. They probably thought: “Why not to generate this check automatically if the variable is nullable?”

JetBrains’ developers did just that and made our lives that much easier for it!

3. You can get rid of util classes

Let’s talk about the ugly things that are util classes. Have you ever had a project without them? We can hardly remember that happening. Kotlin has a smart solution – extension functions – to help you get rid of all your util classes once and for all.

The extension function is almost a usual Kotlin function. But when you declare it, you need to specify the class whose instances will have the extension function.

fun Context.toast(text: String) = Toast.makeText(this, text, Toast.LENGTH_SHORT).show()

Notice ‘this’ that we pass as a parameter to the makeText() method? It’s not an instance of the class where we declare this function, rather it’s a Context instance. And now you can call this function directly from your Activity or any other Context instance. For example:

toast("Hi")

You should remember that the extension function doesn’t modify the class it extends in any way. So how does it work without changing the original class? Let’s see the bytecode one more time.

public final toast(Landroid/content/Context;Ljava/lang/String;)V
    @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
    @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 1
   L0
    ALOAD 1
    LDC "$receiver"
    INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
    ALOAD 2
    LDC "text"
    INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
   L1
    LINENUMBER 31 L1
    ALOAD 1
    ALOAD 2
    CHECKCAST java/lang/CharSequence
    ICONST_0
    INVOKESTATIC android/widget/Toast.makeText (Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
    INVOKEVIRTUAL android/widget/Toast.show ()V
   L2
    LINENUMBER 32 L2
    RETURN
   L3
    LOCALVARIABLE this Ligalata/com/kotlinexample/MainActivity; L0 L3 0
    LOCALVARIABLE $receiver Landroid/content/Context; L0 L3 1
    LOCALVARIABLE text Ljava/lang/String; L0 L3 2
    MAXSTACK = 3
    MAXLOCALS = 3

Ha! Your function implicitly receives the instance of the class it extends as the first parameter. So in the bytecode, any access to ‘this’ in the body of the function is replaced with the access to the first parameter. There’s no magic really. And you can use this function anywhere in your project.

Time to remove your util package!

4. You can forget about view binding

Do you remember the findViewById() method()? We’re sure you don’t like it. We don’t either. Also, we don’t like to declare variables and Butterknife annotations for every view we need to access.

You can forget about view binding with Kotlin Android Extensions. No longer do you need to create variables and bind views. You can access your views directly with their identifiers declared in the xml layout.

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button = (Button) findViewById(R.id.button);
        final TextView text = (TextView) findViewById(R.id.text); 
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                text.setText("You've clicked a button");
            }
        });
    }
}

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
        super.onCreate(savedInstanceState, persistentState)
        setContentView(R.layout.activity_main)

        button.setOnClickListener { text.text = "You've clicked a button" }
    }
}

That’s so much simpler, isn’t it?

Basically, the findViewById() method is still in use. But there’s no need to write it yourself. Kotlin will do it for you.

When you use Android Extensions, the findCachedViewById() function and the HashMap instance will be generated automatically. Every access to your view by its identifier will be replaced with a new function call. If it’s the first time you access the view, this function will call the usual findViewById() function, and will add the received view into the HashMap to retrieve the view from it the next time you access it.

5. You can work with collections much more easily

Let’s talk about collections in Kotlin. Because quite often we need to perform difficult operations with collections of data models. For example, we might have a list of students from which we need to retrieve three students with A grades, and two students with B grades.

Look at the solution in Kotlin:

var students = listOf(Student("John", 0), Student("Julia", 2), Student("Matt", 1),
                Student("Katie", 0), Student("Dan", 0))

var firstList = students.filter { it.mark == 0 }.take(3)
var secondList = students.filter { it.mark == 1 }.take(2)

Here’s how we would solve the same problem in Java:

ArrayList<Student> students = new ArrayList<Student>() {{
            add(new Student("John", 0));
            add(new Student("Julia", 2));
            add(new Student("Matt", 1));
            add(new Student("Katie", 0));
            add(new Student("Dan", 0));
}};

ArrayList<Student> firstList = new ArrayList<>();
ArrayList<Student> secondList = new ArrayList<>();

for (Student student: students) {
            boolean isFirstFilled = firstList.size() >= 3;
            boolean isSecondFilled = secondList.size() >= 2;

            if (isFirstFilled && isSecondFilled) break;
            int mark = student.getMark();
            if (mark == 0 && !isFirstFilled) {
                firstList.add(student);
            } else if (mark == 1 && !isSecondFilled) {
                secondList.add(student);
            }
        }

This is just a small example of how collections are used in Kotlin and in Java, but you can already see the difference! Can you imagine the difference Kotlin makes if we work on collections for a large project?

In our next article we’ll talk about Anko and Anvil in Kotlin and their substitutes in Java. Stay in touch to get more interesting examples of how Java and Kotlin compare!

Tech

A Full List of the Best Frameworks for Building Android Apps

Tech

How to Process Audio for Your Android project

Tech

How We Created uCrop, Our Own Image Cropping Library for Android