Introduction to Kotlin for Java developers: Guide to using classes and coroutines

As Java is one of the classic object-oriented languages, classes and objects are of special interest to Java developers. Object terrain programming provides very powerful capabilities in terms of structuring and organizing programs, especially as the size of the program increases. This is why most modern languages ​​provide objects in some form.

ⓒ Louis Tsai / Unsplash

On the other hand, defining and maintaining classes and hierarchies of classes can become complex, which can slow down programming. A compromise is needed here. Kotlin is one of the relatively new languages ​​that works behind the scenes to simplify programming while still allowing you to access the complexity when you need it.

Class simplification with Kotlin

We looked at examples of classes written in Kotlin in the ‘“Modern Alternative to Java” Introduction to Kotlin Guide’, but this time, let’s look at another example.

data class StarWarsMovie(val title: String, val episode_id: Int,  val release_date: String)

It may be hard to believe, but this one line provides a complete class. This is a data class, equals and toStringCommon methods such as are automatically provided. Other than that, it works the same as any other class. This class models a Star Wars movie using the movie title, ID, and release date.

The point to note here is that Kotlin reduces class creation to its essential elements. In this example, all we provide is the class name and constructor arguments. All other necessary elements are inferred by Kotlin based on the types and names of the arguments. Kotlin classes work just like Java classes, but do not require you to write members explicitly.

Public members and methods

In Kotlin, members and methods are public by default, so you can create an instance of a class and access its properties directly.

val newHope = StarWarsMovie(“A New Hope”, 4, “1977-05-25”)
println(newHope.id) // outputs “4”

There are interesting design differences between Java, which requires visibility declarations, and Kotlin, which assumes publicity. Streamlining like this leads to a simpler and faster language surface. On the other hand, if you want the safety of private members, you must explicitly set the modifier in Kotlin.

To set one of the fields private, you can do this:

class StarWarsMovie(val title: String, private val episode_id: Int, val release_date: String)
println(“test: ” + newHope.episode_id) // Error

function extension

Another example of Kotlin’s dynamism is function expansion (also simply called expansion). Function extensions allow you to add functions to an existing class or interface. It is a well-known feature in the JavaScript world, and stands out in the context of Java-like classes. Here, the existing StarWarsMovie Add a new method to the class.

fun StarWarsMovie.releaseYear(): Int {
val year = release_date.substring(0, 4)
return year.toInt()
}
val newHope = StarWarsMovie(“A New Hope”, 4, “1977-05-25”)
val releaseYear = newHope.releaseYear()
println(“The release year of A New Hope is $releaseYear”)

In the example above releaseYear()A new method was defined in a class called . existing class StarWarsMovie.releaseYear()Note that it is defined directly in . You can do this with your own class, but you can also use classes imported from third-party libraries. The Kotlin documentation provides examples of adding methods to the standard library. (I tend to be wary of this kind of monkey patching, but I can definitely see Kotlin’s flexibility.)

now StarWarsMoviecast Movie Consider the case where you want to create a subclass of a superclass. In Kotlin, you can do this:

open class Movie(val title: String, val releaseDate: String) {
open fun releaseYear(): Int {
val year = releaseDate.substring(0, 4) return year.toInt()
}
}

class StarWarsMovie(title: String, episodeId: Int, releaseDate: String) : Movie(title, releaseDate) {
val episodeId: Int = episodeId
}

open A keyword indicates that a class or function can be used for subclassing or overriding. In Java terms, Kotlin classes are basically finalam. basic public Members and Basics final Classes can be interpreted as encouraging a preference for composition over inheritance. In the previous example StarWarsMovieand Movieused constructor-based declaration. : movieThe colon indicates an extension, and in Java extends It works similarly to keywords.

explicit class declaration

In Kotlin, similar to Java, there is also the option to declare more explicitly.

class Movie {
var title: String = “”
var releaseDate: String = “”

constructor(title: String, releaseDate: String) {
this.title = title
this.releaseDate = releaseDate
}

fun releaseYear(): Int {
val year = releaseDate.substring(0, 4)
return year.toInt()
}
}

class StarWarsMovie(title: String, releaseDate: String, val episodeId: Int) : Movie(title, releaseDate) {
// …
}

The class you see here is one that needs no explanation to Java developers. constructor is not the class name constructor It is defined using keywords. If you need an alternative constructor, you can combine this with the default constructor style we saw earlier.

class Movie(title: String, releaseDate: String) {
var title: String = title
var releaseDate: String = releaseDate

constructor(title: String) : this(title, “”) {
println(“Secondary constructor called”)
}
}

In general, Kotlin’s object-oriented syntax is very easy to mix. Choose simpler syntax when possible, and expand to more detailed syntax when absolutely necessary.

Regarding the constructor concept, you can use the init keyword provided by Kotlin to execute code during object creation. Other than Init, only property declarations are allowed. Kotlin differs from Java in that it separates logic and properties during creation.

class StarWarsMovie(title: String, episodeId: Int, releaseDate: String) : Movie(title, releaseDate) {
val episodeId: Int = episodeId
init { println(“Made a new star wars movie: $episodeId”)  } // un-Java-like init block
}

temporary class declaration

One particularly liberating improvement in Kotlin is the ability to temporarily declare types, classes, and functions anywhere in the code. Therefore, if you think, “I need a class for this part,” you can simply use the class right away. You can later go back to the entire operation at the same point and use the new class. You can come back later and refine the class again, or extract it into its own file like you would in Java.

Singleton style object

In Kotlin: object You can declare a singleton-style object with keywords.

object MovieDatabase {
private val movies = mutableListOf()

fun addMovie(movie: Movie) {
movies.add(movie)
}

fun getMovies(): List {
return movies.toList()
}
}

Objects like this are not nested within other blocks and can only be declared at the top level.

Singleton means that there is only one instance globally in any given program execution. This is a common pattern in Java and Java-based frameworks (Spring, etc.). There are multiple references but they are all the same MovieDatabaseSee .

val db = MovieDatabase;
db.addMovie(movie)
db.addMovie(starWarsMovie)

// some other remote part of your app:
println(“Got the same instance: ${db.getMovies()}”)

The key here is that they all receive handles to the same instance. If you run this code like this, you will get ugly output like this:

Got the same instance: Movie@3b764bce StarWarsMovie@759ebb3d

The first thought that comes to mind is MovieMake it a data class and toString() You might be getting the method for free, but the data class is finalam. A better way is simple toStringis to add it directly.

open class Movie(val title: String, val releaseDate: String) {
override fun toString(): String {
return “Movie(title=”$title”, releaseYear=${releaseYear()})”
}
}

Now you can get better output.

Got the same instance: Movie(title=”The Shawshank Redemption”, releaseYear=1994) Movie(title=”A New Hope”, releaseYear=1977)

Kotlin concurrency and coroutines

Concurrency is another area where Kotlin provides interesting options. Java has been very actively improving its concurrency model recently, and some of the improvements are inspired by Kotlin’s coroutines. Coroutines provide a different kind of access to threading. One of the mechanisms used here, structured concurrency, is now being added to Java.

Coroutines and structured concurrency keep thread management in a synchronous, declarative style. Let’s look at a simple example taken from the Kotlin documentation.

import kotlinx.coroutines.*

fun main() = runBlocking { // this: CoroutineScope
launch { doWorld() }
println(“Hello”)
}

// this is your first suspending function
suspend fun doWorld() {
delay(1000L)
println(“World!”)
}

suspend The keyword is doWorld Indicates that the function can be paused by the concurrency engine. In other words, the method is a non-blocking method. main function runBlocking You can see that it is set to a function, which is blocking CoroutineScopeSet . All concurrent code is CoroutineScope must be declared within

The actual mechanism for running a thread is launch It’s a keyword. This keyword is main In the method doWorldIt is used to call .

Of course, Kotlin’s concurrency and coroutines covered here are only at the surface level, but through examples, you will be able to get a feel for how coroutines work in relation to Java. As mentioned earlier, Java is actually moving closer to Kotlin in this area. Another important aspect of Kotlin concurrency is the JVM’s introduction of virtual threads. This feature suggests the possibility of using Kotlin’s coroutines in conjunction with the JVM’s virtual threads.

conclusion

Kotlin modifies several rules of Java to increase flexibility and improve areas that can cause coding delays. While Java code tends to be strict and explicit, Kotlin is closer to flow and convention. The good thing is that Kotlin provides a modern, highly expressive language that can be used alongside Java on the JVM. I think most Java developers will be positive about what Kotlin can do.
editor@itworld.co.kr

Source: www.itworld.co.kr