From Java to Kotlin

android ios programming development dart flutter


This year, 2017, has been the year that Kotlin has made a big splash in the Android development community. At Google's I/O developer conference, Google announced that Kotlin was going to join Java as an official language of Android. This article's goal isn't to convince you that Kotlin is fantastic (it is though), or that you should use it in your projects (you really should). The goal here is to help you bring your existing projects up to speed with Kotlin. We'll take an existing project, written in Java, and show you how to update it to Kotlin. All while demonstrating how Kotlin and Java can coexist in the same project. So you can update your project piece by piece instead of completely re-writing your project from scratch. Our sample project, Tasty Versions, displays the dessert codenames of all the Android versions with a RecyclerView. When you tap on one of the versions in the list it displays a Toast message with the version numbers and the name of the version. There's also a splash screen. It's entirely written in Java. Before I get started I wanted to share two snippets of code. One is in Java and the other in Kotlin. Both snippets accomplish the same thing. With the Kotlin snippet you can see that it's a bit more concise, a bit easier to read, and readable to those that have been developing in Java for a while. class JavaActivity extends AppCompatActivity { private final String className = "JavaActivity"; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_java); Context context = this; Button btnAction = (Button) findViewById(R.id.btn_java); btnAction.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(context, String.format("Clicked in %s", className), Toast.LENGTH_SHORT) .show(); } }); } } class KotlinActivity : AppCompatActivity() { private val className = "KotlinActivity" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_kotlin) val context: Context = this val btnAction: Button = findViewById(R.id.btn_kotlin) btnAction.setOnClickListener { Toast.makeText(context, "Clicked in $className", Toast.LENGTH_SHORT).show() } } } ========= As we can see between these two different Activities there is a lot that's the same. Both extend AppCompatActivity*, both have a class member for the className, and both override the onCreate method and set a button's OnClickListener. In the Kotlin version there are no annotations. It also doesn't require an annonymous inner class to handle the button's OnClickListener. Looking at the two and not knowing any Kotlin you can see exactly what's going on. The added bonus is that it looks like it's a bit easier to read. The biggest bit of strangeness happens on lines xx where we define our variable as val context: Context = this and val btnAction: Button = ... In Kotlin you declare varaiables with either 'val' or 'var'. A 'val' declaration means that whatever you're assigning to will not change. And if you guessed that 'var' declarations mean that whatever you're assigning can change, well you'd be right. Looking at these two lines we see value declarations but after the name of the value we see a ": Context" and ": Button". This is how we define the type of our value. On line xx we declare that context is a value of type android.content.Context and to that we assign the instance of our activity via 'this'. On line xx+1, we declare that btnAction is value of type android.widget.Button and we assign to that the results of a findViewById. One of the neat things about assigning to vals and vars is that you won't see the 'new' keyword anwyhere. More on that later though. One last great thing about Kotlin is that it is a statically typed language. That means that type checking is done at compile time instead of run time. Type checking at run time is dynamically typed. Because Kotlin is statically typed, the compiler will let you know of issues in your code where a variable may not be set, but is accessed causing a NullPointerException at run time. That's not saying that you can't experience a NullPointerException in your Kotlin code. You can, but you'd be forcing it to happen. You can read more about that in Eunice Obugyei's "Kotlin for Android: An Introduction" article in the Null Safety section (https://www.raywenderlich.com/132381/kotlin-for-android-an-introduction). OK, so we've briefly seen a little Kotlin. Let's download our sample project written entirely in Java. This project will serve as our starting point, that would represent your real world project, as we update it // TODO here are the instructions for downloading the project. Please make sure you are on the latest version of Android Studio 3.0. Our sample project has a few straightforward classes: * **TastyVersionsApplication.java** for our custom application class. * **SplashActivity.java** to handle our splash screen. * **TastyVersionsActivity.java** to display our RecyclerView and show the versions. * **TastyVersionModel.java** to model our Tasty Version. * **VersionItemViewHolder.java** to describe our Tasty Version item view. * **VersionItemAdapter.java** to bind our RecyclerView.ViewHolder to the TastyVersionModel at position.. Let's start out with the simplest class, **SplashActivity**, and convert it from Java to Kotlin. We'll use an automatic conversion process to do this instead of rewriting our class by hand using Kotlin. With our **SplashActivity.java** file open, click on **Tools > Convert Java File to Kotlin File**. On the Mac the short cut is Command + Shift + Option + K and on Windows it's Control + Shift + K. :: Snapshot of the tools menu Now that we've converted the class to Kotlin let's check out the bytecode our updated class would [When targeting the JVM, Kotlin produces Java compatible bytecode](https://kotlinlang.org/docs/reference/faq.html#what-does-kotlin-compile-down-to). Since the bytecode Kotlin produces is Java compatible we should be able to check it out and maybe some of us might be able understand it. The rest of us can decompile it back into Java and see what's going on. Let's inspect the bytecode of **SplashActivity.kt**. To do this, click on **Tools > Kotlin > Show Kotlin Bytecode**. This will display the bytecode of the Kotlin file we just created. Interesting, but if you don't read bytecode, this isn't very helpful. Let's go ahead and decompile the bytecode. To do this, click on the **Decompile** in the bytecode tab. :: Bytecode tab You'll now notice a new editor tab opens up with the decompiled Kotlin bytecode. Let's now compare the decompiled bytecode of our Kotlin activity with our original version that was written in Java. You'll notice that it's fairly similar to what we had originally. :: Side by side compare Up top in the generated Java file we have a @MetaData annotation. The @Metadata annotation gets produced by the Kotlin compiler and helps provide information like the original class name, package name, etc. to the compiler and for reflection. The shortened field names are to conserve on file size but here's a quick chart of what they stand for. mv: Metadata version bv: Version of the bytecode interface k: Kind of class this metadata represents d1: Custom format metadata d2: Array of strings that occur in metadata. @Metadata( mv = {1, 1, 9}, bv = {1, 0, 2}, k = 1, d1 = {"\u0000\u001e\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0010\u000e\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002J\u0012\u0010\u0005\u001a\u00020\u00062\b\u0010\u0007\u001a\u0004\u0018\u00010\bH\u0014R\u000e\u0010\u0003\u001a\u00020\u0004X\u0082D¢\u0006\u0002\n\u0000¨\u0006\t"}, d2 = {"Lcom/raywenderlich/tastyversions/activities/KotlinActivity;", "Landroid/support/v7/app/AppCompatActivity;", "()V", "className", "", "onCreate", "", "savedInstanceState", "Landroid/os/Bundle;", "production sources for module app"} ) The next bit of odd additions are the **public View _$_findCachedViewById(int var1)** and **public void _$_clearFindViewByIdCache()** methods. One of the extras that Kotlin Android Extensions provides is the ability to cache the views that are loaded by calling **findViewById**. Once they're loaded they're added to a cache where they can be accessed a bit more quickly and efficiently rather than reloading them. That cache by the way is represented by the **private HashMap _$_findViewCache;** field. ... Let's give this a go with our main activity, the TastyVersionsActivity class. This class is a bit more complicated. We have our onCreate method, our onResume method and a setupUI method that takes care of setting up the RecyclerView. Open the **TastyVersionsActivity.java** file. Then, on the toolbar, click on **Tools > Convert Java File to Kotlin File**. :: Snapshot of Tools-> on TastyVersionsActivity Looking at the Kotlin file we again see a very recognizable code. We're missing some semicolons, some **new** keywords and where we set our binding inside of **onCreate** looks a little bit different. :: Compare java to kotlin of TastyVersionsActivity Remember earlier when we mentioned that Kotlin produces bytecode compatible with Java? Well that's going to come in handy now. Now we'll start up an emulator and run our app. Our project, in our current state, has both Java code and Kotlin code. With no extra effort we converted some of our app to Kotlin and everything is running smoothly. Kotlin's ability to compile into Java compatible bytecode is one of the great features of Kotlin. For those of us working on real world projects this feature is going to let us slowly, but surely, introduce Kotlin into our Java based Android projects.

Modeling your data with Kotlin

Modeling your data is an important part of any software project. We model our data to mimic relationships between objects and represent our database. For example, in Tasty Versions, we model our version with the **TastyVersionModel** class. package com.raywenderlich.tastyversions.models; public class TastyVersionModel { private String name; private String apiVersion; private int imageResource; public TastyVersionModel(String name, String apiVersion, int imageResource) { this.name = name; this.apiVersion = apiVersion; this.imageResource = imageResource; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getApiVersion() { return apiVersion; } public void setApiVersion(String apiVersion) { this.apiVersion = apiVersion; } public int getImageResource() { return imageResource; } public void setImageResource(int imageResource) { this.imageResource = imageResource; } } Our model allows us to have a name of type String, an API version of type String and a resource ID reference to an image that represents our version. We have a constructor that takes, as parameters, all of these values. Then we have matching getter and setter methods for each of our class fields. These 36 lines of code define what we call our Tasty Version model. That's fairly compact, but it's also a very simple class. Let's take a look at what this looks like if we were to write this class out in Kotlin. package com.raywenderlich.tastyversions.models data class TastyVersionModel(var name: String, var apiVersion: String, var imageResource: Int) That's it! With Kotlin, we can define our simple Tasty Version model by defining our primary constructor as well as our class fields with one line of code. There's something we've not seen before and that's the **data** keyword. Adding the data keyword to the class definition makes this a **data class**. This keyword tells the compiler that we want some extra functionality without having to explicitly code it. Namely, we want the following methods:
  1. equals()
  2. hashCode()
  3. toString()
  4. copy()
  5. componentN()
Our TastyVersionModel doesn't override the default **equals()**, **hashCode()** or **toString()** because for our usage it wasn't necessary to define since we weren't sorting, comparing or printing any of our instances. In the real world, your model objects will override equals() and hashCode() so that you can use your business logic to determine if two different instances are equal to each other, Manually creating a data class to replace your large model java files. We’ll replace our object definition with a data class representation of the Java class that models the Android version. Will detail how using a data class we get the following methods for free: equals() hashCode() toString() copy() will omit componentN() methods as there is no need to get confusing referencing fields by the number in which they’re declared. Will cover creating a constructor primary constructor needs at least one parameter parameters for a constructor need to be marked as val or var. Will briefly explain what val and var are in a “Note’s from the author section" Will cover how data classes cannot be abstract, sealed, open, or inner. The autotranslate feature of copying and pasting Java code into a Kotlin file. A basic demonstration with an animated gif copying a simple method of Java into a .kt file and showing Android Studio prompt for the conversion, then the conversion itself. Wrap up with the overlaying message of Kotlin is fantastic and you don’t have to drop your current project and move to Kotlin at once. Migrating to Kotlin can be as slow or as fast a process as you (or your manager) allows. Finally, provide links to other team written tutorials on Kotlin.