There is a mobile developer in the forest, he sees - Kotlin is on fire. Sat in Kotlin and burned
The world is going crazy. They say that all new mobile projects on Android are written exclusively on Kotlin. Nowadays, it is very dangerous not to learn new technologies. At first, your knowledge becomes obsolete, you take off from work, live near a heating main, fight with homeless people for food and die in obscurity without learning a functional programming. Therefore, I went to Kurslin to study the course of Kotlin for Java Developers and began to read a book (hello, abreslav , yole ), asked around the friends you know from where and came back with a certain emptiness in my heart. Help Oleg the traveler find meaning in Kotlin!
- Bonus: “How do you use Kotlin?” Habros
● In Java, you more often understand by narrow context what is happening. a = b
- write to field or locale, a[1] = 2
- write to array. In Kotlin, behind any simple expression can be arbitrarily complex code due to all sorts of cleverness, like overloading . Without IDE you will understand nothing. And IDE is bad when you ride the train and see that the swing interface is sucking the laptop out of a laptop like a vampire.
● Kotlin gives the same API for collections and sequences, which is why people abuse map / filter chains on collections, creating a bunch of intermediate non-lazy copies. Stream in Java specifically introduced to distinguish between a lazy and non-lazy collection. Yes, there is an inspection in the IDE for this - because inspections are designed to correct the shortcomings of languages.
● Speaking of IDE. How good is Kotlin support in IntelliJ IDEA? Is it really better than Java? There are big doubts. Maybe someone from JB has enough spirit to advocate on this issue.
● Kotlin Forsyth use it
, which results in unreadable code. Something like seq.map { it -> foo(it, 1); }.map { it -> bar(it, 2); }.filter { it -> it.getBaz() > 0; }
. What was it all about? Names of variables are not given in vain! And here it turns out a monologue like “Let's take it, fasten it to it, then we will twist it and if it became more than that, then we put the hinge on top”.
● Chains like ?.let { foo(it); }?.let { bar(it); }
this is generally hell and should be banned in the declaration of human rights. And this is considered idiomatic, Karl. Unlike normal if. It is impossible to read such a code.
● From the interop with Java blood comes from the eyes. And then all sorts of JvmStatic and JvmName, and the code turns into a circus with horses.
For example, here we have this:
classC{
companionobject {
@JvmStaticfunfoo() {}
funbar() {}
}
}
Regarding the annotated method, the compiler generates both a static method in the class external to the object, and an instance method in the object itself. Possible options:
C.foo();
- worksC.bar();
- syntax error, because the method is not staticC.Companion.foo()
; - the instance method remainsC.Companion.bar();
- the only correct way
Recover from the beauty of the decision? Okay, let's go further. Now you are ready to understand and accept the fact that, for example, you cannot simultaneously declare two such methods:
fun List<String>.filterValid(): List<String>
fun List<Int>.filterValid(): List<Int>
After all, their signatures at the JVM level are the same: filterValid(Ljava/util/List;)Ljava/util/List;
Therefore, you need to push a special crutch:
fun List<String>.filterValid(): List<String>
@JvmName("filterValidInt")fun List<Int>.filterValid(): List<Int>
And how about this: in Kotlin there are no checked exceptions. And in Java reality they are. The special purpose unit "Combat prostheses" has the honor to introduce a new self-propelled crutch @Throws
:
@Throws(IOException::class)funfoo() {
throw IOException()
}
One can argue for a long time that “javists are constantly whining, that everything is not like in Java”. But if this is beautiful, then what is terrible?
In general, it is recommended to open the Java-to-Kotlin Interop article and see for yourself how it looks.
● Automatic getters / setters with the addition of the English word get and the first letter are peried in a large register (apparently, in the ENGLISH locale? After all, the register of letters is system-dependent) is scary.
import java.util.Calendar
funcalendarDemo() {
val calendar = Calendar.getInstance()
if (calendar.firstDayOfWeek == Calendar.SUNDAY) { // call getFirstDayOfWeek()
calendar.firstDayOfWeek = Calendar.MONDAY // call setFirstDayOfWeek()
}
if (!calendar.isLenient) { // call isLenient()
calendar.isLenient = true// call setLenient()
}
}
● Extension-methods pollute the public interface with such things that the author was afraid to think about.
Since this feature is not in Java, I will explain. You can write any method, put the name of the “receiving class” on the left, and that’s all - it’s expanded. Let's expand the MutableList
function swap
:
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // 'this' относится к листуthis[index1] = this[index2]
this[index2] = tmp
}
val lst = mutableListOf(1, 2, 3)
lst.swap(0, 2) // 'this' внтури 'swap()' будет иметь значение 'lst'
The work of extension methods is possible, even if the author specifically made the final class, clearly showing that he does not want third-party extensions. It turns out something like rape with particular cynicism. And of course, they break compatibility: what will happen if in the next version of the library the author adds methods with the same names, but with a different return type? Should he think about all extension methods that any people can add to the same class?
In addition, it is not possible to make optimized implementations of extension methods in specific subclasses. Although it would seem, this feature could produce a wow effect.
● Library in some places not thought out. For example, reduce.
Here is what reduce looks like:
listOf(1, 2, 3).reduce { sum, element -> sum + element } == 6
There is only a form with identity ( fold ), but it is not always applicable.
listOf(1, 2, 3).fold(0) { sum, element -> sum + element } == 6
By the way, why does Habr highlight these two lines differently? Ahhh, it doesn't matter anymore.
In fact, fold and reduce do the same thing, but fold requires a certain initial value, and reduce uses the first element of the list as this initial value. Accordingly, a form without identity throws an exception for an empty collection.
Is this behavior always necessary for everyone? Why not return some Optional
and let the user decide what to do in the case of an empty collection? Yes, or even return null, since this is a null-friendly language.
● Let's load more about the library. Haha in standard library of language which supports data classes, included pairs? This is a direct promotion of bad code.
I remind you that data classes look like this:
dataclassUser(val name: String, val age: Int)
val duncan = User("Duncan MacLeod", 426)
val (name, age) = duncan
println("$name, $age years of age") // печатает "JaDuncan MacLeodne, 426 years of age"
The couple looks like this:
val (name, age) = Pair("Java", 23)
println("$name, $age years of age") // тоже печатает "Java, 23 years of age"
And all because inside:
publicdataclassPair<out A, out B>(
publicval first: A,
publicval second: B
)
It is quite obvious that the average bydlokoder will score to write their classes on the second day of use, and the code will turn into a horrible parody of lisp. We fold cucumber zhopki with hundreds of oil, we write filters-frankensteins and in production. Fast, easy, unreadable.
● Very strange point - the possibility not to specify the return type of the method (especially public).
Most recently there was a case in C ++, from which I was almost torn apart by anger. The program fell in an arbitrary place, but I did not understand why. It turned out that in C ++ you can not write return in the method, which, according to the signature, should return something. This is not a syntax error according to the standard, but undefined behavior. Accordingly, the program in rantayme falls with an arbitrary error. Wonderful language - it has a special syntax for non-working methods. Since then, I have very carefully checked that we promised to return from the method and that we gave it to the output. Such a paranoid habit.
And now, in the best in the world language Kotlin, we can not specify the return type at all. This provokes people to write inarticulate noodles, in which nothing is clear. If a method a
calls a method b
, and that method c
, and that contains an expression in the body when
, in which three more methods are called in the branches d
, e
and f
try to understand the type of the method а
!
funa(check: Int) = b(check)
funb(check: Int) = c(check)
func(check: Int) =
when (check) {
1 -> d()
2 -> e()
else -> f()
}
fund() = "result 1";
fune() = "result 2";
funf() = "result 3";
funmain(args: Array<String>) {
println(::a.returnType)
for (i in1..3) println(a(i).javaClass.name)
And at first everything seemed to be simple and clear, and in the process of evolution it changed, and kapets. You change the return type of the method f
, and you automatically change the return type of the method in a а
completely different package, and you do not understand what is happening.
Initially, in our example, the exhaust looked like this:
kotlin.String
java.lang.String
java.lang.String
java.lang.String
But it is worth changing the definitions of functions to these:
fund() = "1";
fune() = 100500;
funf() = listOf<String>();
And the result will immediately change to
kotlin.Any
java.lang.String
java.lang.Integer
kotlin.collections.EmptyList
No crystallization API. For public methods, the explicit API specification must be a sacred cow, and Kotlin does not require it.
Conclusion
Perhaps, for a start is enough. From this article it may seem that everything in Kotlin is bad, but this is obviously not the case. At a minimum, the salary of a Kotlin developer is usually not bad :-)
In a recent report on Joker 2018 ( there are slides ), Pasha ( asm0dey ) Finkelstein noted that on the Kotlin backend it helps to write more beautiful and concise code (but not always it works), it produces more expressive tests, it works with GraalVM, and All this with examples for Spring, Spring Security, Spring Transactions, jOOQ, etc.
Should I switch to Kotlin from Java for mobile applications? It is not clear. Anyway, Kotlin is interesting. Let's dig in it!
Minute advertising. Already this week, December 8-9, 2018, the Mobius conference will be held . On it, Svyatoslav Shcherbina from JetBrains will talk about how to write mobile applications on the Kotlin Muplitplatform . In addition, you can cross with a lot of people who actually use Kotlin, and find out why and how they do it. There is still space, but there is almost no time left, so if you want to come, you now have one last chance. Tickets can be purchased on the official website .
Only registered users can participate in the survey. Sign in , please.