Kotlin character

Original author: Marcin Moskala
  • Transfer
Hello, Habr! We hope to get to Kotlin in the foreseeable future. They could not get past this article (February).

We read and comment!

I just finished reading Bruce Tate's book “ Seven Languages ​​in Seven Weeks ” - and since then I love it! Although I have experience working with most of the languages ​​described in the book, I really liked how the author characterizes the languages ​​and how these characteristics ultimately affect the practical use of the language.

Therefore, I decided to write another chapter for this book, an additional one. It will be devoted to a language that I know well enough, I understand its advantages and disadvantages: this is Kotlin.

The purpose of this article is not to teach you the Kotlin language, but to show its character. Do not try to understand her thoroughly. Better pay attention to how the features described here could affect your programming style.

A typical feature of Kotlin is that this language, in essence, does not introduce anything that has not yet been encountered in other programming languages. The fact is that Kotlin uses all these old finds in the most masterpiece way. Remember the superhero of Iron Man. Tony Stark assembled Iron Man from the simplest electronic components. Iron Man does not have such characteristic superpowers as Superman or Flash. This may seem like a weakness, however, in the long run this is a huge advantage. Let's talk about it below, but for now, let's start with the basics.



The basics


It is believed that Kotlin is intended for programming in one or another IDE - for example, in IDEA IntelliJ, Android Studio or CLion (Kotlin / Native). Here we start from the command line to demonstrate Kotlin in a simpler context. After installing Kotlin , run REPL (interactive shell) like this:



Let's try with numbers:

>>> 1
1
>>> 1 + 2
3
>>> 1.0 + 2
3.0

Simple math works. This is Kotlin / JVM running in a Java virtual machine. Integers in Java are primitives. And in Kotlin? Let's check the type of number:

>>> 1::class
class kotlin.Int
>>> 1.0::class
class kotlin.Double

Both are objects! In general, in Kotlin all entities are objects. What about java? Kotlin is fully interoperable with Java, but we see that the above types are Kotlin types. The fact is that some Kotlin built-in types cover Java types. You can see what the Java type will be in this case, using the property javafrom Classor the property of javaClassany object:

>>> 1.0::class.java
double
>>> 1.0.javaClass
double

This is double! Double numbers in Java are primitives. How is this possible? Kotlin employs an optimization that allows primitives to be used instead of objects when no object-oriented features are used. This happens under the hood, it does not concern the developer at all. If we need to use it doubleas an object, then it will be used instead Double. From the point of view of the developer, we can still say that "everything is an object." Define several properties:

>>> val a = 1

This property is read only. You can also define a property for reading and writing using var:

>>> var a = 1

Please note: the type is not specified here. However, do not be fooled by this: Kotlin is a language with strong static typing. The type of the property is simply inferred from the type of the assigned value:

>>> ::a.returnType
kotlin.Int

Enough math, let's move on to more interesting features.

Security


Iron man was constructed because neither the police nor the army could rescue Tony Stark from the captivity of the terrorists. Tony made Iron Man to take care of his own safety, as well as expand his capabilities. He also became famous for this.

In the same way, JetBrains created Kotlin. The same company creates the most popular integrated development environments. All of their tools were originally created in Java, but the JetBrains team in practice felt all the flaws of this language. Then the company began to experiment with other languages, for example, Scala or Groovy, but even they did not satisfy them. Therefore, in the end, JetBrains decided to create their own language, with the aim that this new language should provide maximum security (so that there are no errors in the products) and scalability. In addition, Kotlin excellent propiaril JetBrains. They were already known all over the world, and when a community of specialists appeared who decided to use their awesome language, JetBrains became even cooler for them. (Here I retold this story only in the most general terms.this podcast ).

Kotlin significantly outperforms Java in terms of security. Properties must be initialized:

>>> var a: String
error: property must be initialized or be abstract

By default, types are not nullable:

>>> var a: String = null
error: null can not be a value of a non-null type String

If we want to show that this type is nullified, this is done with ?:

>>> var a: String? = null

True, a nullable type cannot be explicitly used:

>>> a.length
error: only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?

Nullable types are similar to others, such as the optional types used in Scala. Before use, this type must be unpacked. You can use a secure call that acts just like a normal call if the property is not equal null. If the property is equal null, then we do not call the method, but simply return null:

>>> a = null
>>> a?.length
null
>>> a = "AAA"
>>> a?.length
3

We can also use an unsafe call that will throw an exception if the property is zero;

>>> a = null
>>> a!!.length
kotlin.KotlinNullPointerException

It is not recommended to work with unsafe calls, so you need to resort to them only in case of emergency. In projects on Kotlin they are very rare. This is a huge difference from Kotlin Java, where all calls are unsafe.

Intelligence


Iron Man is so cool because his combat suit is truly intelligent. He calculates the situation and warns Tony about the dangers. Kotlin is also genuinely intelligent and helps developers a lot.

One example of such intelligence is smart casting. When we check if a property is not equal to zero, we can use it as if it is really non-zero. To demonstrate some more advanced features, we will work with files. I suggest using IDEA IntelliJ ( it’s described here how to get started with it). Alternatively, you can try all these features in the online REPL . Consider an example:

fun smartCastingExample(str: String?) {
    if(str != null)
        print("Length is " + str.length)
}

As you can see, str is used explicitly (without unsafe or secure calls). That's why in the test if(str != null)type is driven by String?a String. It will also work if we exit the function with the opposite check:

fun smartCastingExample(str: String?) {
    if(str == null)
        return
    print("Length is " + str.length)
}

The principle does not only work with nullability. Smart casting is also possible:

fun smartCastingExample(any: Any?) {
    if(any is String)
        print("String with length " + any.length)
}

Kotlin is perfectly supported in IDEA IntelliJ, Android Studio or CLion. In these IDEs, you get tons of tips, tricks, and support. Here is an example where the imperative processing of a collection, typical of Java, is replaced by the declarative one characteristic of Kotlin. Please note that it is the development environment that offers and performs the entire transition:



Minimalism


Tony Stark does not put on Iron Man as a whole if he does not need it. Usually he uses only the machine or some small components.



Kotlin's philosophy, in particular, postulates that the simple must remain simple. Here is the Hello World code on Kotlin:

fun main(args: Array) {
    print("Hello, World")
}

This is just one function that displays text. Other typical Kotlin operations are also simple. When we do not need the whole body of a function, we can do with a single expression:

fun add(a: Int, b: Int) = a + b

Below you will see more than once the exact same minimalist style.

Flexibility


Iron Man loses to Superman in some important ways. For example, laser eyes are given to Superman from birth, he can incinerate the enemy with his eyes when he pleases. Tony Stark did not equip Iron Man with eye lasers - perhaps because he did not see this as an urgent need. It is important to note here that he could easily add such a feature to Iron Man. In fact, this is also within the power of any Iron Man user. But, at the same time, other upgrades can also be hung on Iron Man, which may well be no less effective, but will cost less. This is a huge potential for flexibility. Let's move on to practice. Most programming languages ​​provide some form of collection literals. In Python, Ruby, or Haskell, a list can be defined like this:[1,2,3]. Kotlin does not have such collection literals, but it provides top-level functions (those that can be used everywhere), and the Kotlin standard library has such top-level functions with which you can create collections:

>>> listOf(1,2,3)
[1, 2, 3]
>>> setOf(1,2,3)
[1, 2, 3]
>>> mapOf(1 to "A", 2 to "B", 3 to "C")
{1=A, 2=B, 3=C}

Why is this so important? When a collection literal is provided in the language, it also determines how the user will use the collections. All collections have some characteristics. There is an extensive discussion about which lists are better - mutable or immutable? Variables are more efficient, but immutable are much more thread safe. There are many opinions and arguments on this subject. With this in mind, would you like the list literal to generate either a mutable or immutable list? In any case, you will somehow impose on the programmer options for using the language, since the programmer will prefer to use the collection literal. Kotlin in this case leaves the freedom of choice. listOf, setOfand mapOfgive immutable collections:

>>> var list = listOf(1,2,3)
>>> list.add(4)
error: unresolved reference: add
list.add(4)
     ^
>>> list + 4
[1, 2, 3, 4]

Although, it is not difficult to create a mutable collection with mutableListOf, mutableSetOfand mutableMapOf:

>>> mutableListOf(1,2,3)
[1, 2, 3]
>>> mutableSetOf(1,2,3)
[1, 2, 3]
>>> mutableMapOf(1 to "A", 2 to "B", 3 to "C")
{1=A, 2=B, 3=C}

Please note: anyone can define their own collection, and then a top-level function that will create it:

fun  specialListOf(vararg a: T): SpecialList {
    // Код
}

It is easy to see above that I used a generic type parameter T. Do not worry. This means that you need to pass a set of elements of the same type in order to create a collection of this type.

Due to the fact that Kotlin uses basic features, rather than built-in literals, third-party libraries when working with Kotlin are not inferior in strength to its standard library. Another great feature that significantly democratizes libraries and frees developers hands is the so-called extension function. The bottom line is: you can define such a function so that it acts as a method:

>>> fun Int.double() = this * 2
>>> 2.double()
4

Please note: in practice, no methods are added to the class. Extension functions are simply functions that are called in this special way. This feature may seem simple, but it is really powerful. For example, Kotlin, like other modern languages, provides functions for processing collections:

class Person(val name: String, val surname: String)
val avengers = listOf(
        Person("Tony", "Stark"),
        Person("Steve", "Rogers"),
        Person("Bruce", "Banner"),
        Person("Thor", "")
)
val list = avengers
        .filter { it.surname.isNotBlank() }
        .sortedWith(compareBy({ it.surname }, { it.name }))
        .joinToString { "${it.name} ${it.surname}" }
print(list) // Выводит: Брюс Баннер, Стив Роджерс, Тони Старк

Kotlin's huge advantage is that such and similar functions are defined as extension functions. For example, take a look at the implementation filter:

inline fun  Iterable.filter(
    predicate: (T) -> Boolean
): List {
    return filterTo(ArrayList(), predicate)
}
inline fun > Iterable.filterTo(
    destination: C, predicate: (T) -> Boolean
): C {
    for (element in this) if (predicate(element)) destination.add(element)
    return destination
}

If it is not worth implementing, then you can define this function yourself. If you need any other function for processing collections, then you can easily define it. Developers often do this. For example, you can find a lot of libraries that define extension functions for Android. They simplify development for Android. Another result of this decision - at your service there are simply a lot of methods for processing collections.

It is also important to note here that filterthis is a function that extends not the type List, but the interface Iterable:

public interface Iterable {
    public operator fun iterator(): Iterator
}

You can easily define your own collection class that implements Iterable, and the language for you will add these methods to it for processing collections. Even Stringimplements it. That's why you can use all methods of processing collections and with String:

>>> "I like cake!".map { it.toLowerCase() }.filter { it in 'a'..'z' }.joinToString(separator = "")
ilikecake



Brief Summary


Tony Stark was not born a superhero, and no radioactive spider bit him. He designed Iron Man thanks to his incredible knowledge and experience. Likewise, JetBrains is a company that creates great IDEs for different languages; her people learned a lot from this work and embodied their knowledge by writing an incredible programming language. They did not bring anything new to the world of programming, but simply offered a perfectly worked out language that uses the advantages of many other programming languages ​​- therefore, it maximizes the productivity of the developer and the highest quality of projects.

A great example of such smart design is the Kotlin tuples. When the language still existed in beta, it already supported tuples. Although, when the Kotlin team analyzed exactly how working with tuples affects the development of programs, it turned out that their influence is not always positive. That's why tuples weren’t in the release. Instead, annotations can be applied in Kotlin data. It is more universal, and the Kotlin developers had no doubt that in general it would positively affect the work with the language. Tuples are still being discussed, and perhaps someday native support for them will appear in Kotlin. However, for this to happen, the Kotlin team and the development community must make sure that such a solution is feasible.

This eloquently characterizes Kotlin. This is not a hodgepodge of ideas, but a truly competently made language. It contains a minimal set of the most powerful features, which in total give you amazing opportunities. In general, Kotlin leaves you much more freedom - including for co-creation.

Home reading


If you want to understand Kotlin in more detail, I recommend reading the language documentation , as well as getting acquainted with the Kotlin Koans resource . If you're interested in Kotlin for Android, check out this book .

Also popular now: