Kotlin: two fly in the ointment

    The appearance of Kotlin is an important bonus for developers. A high-level language that integrates seamlessly with Java greatly enhances the capabilities of programmers. However, in any language we constantly face some troubles, which, on the contrary, create limitations, and Kotlin, of course, is no exception. We will talk about them today.



    Kotlin was enthusiastically received by the community, as the new programming language greatly simplifies writing code, replacing Java with itself. This is especially noticeable on Android, where new versions of Java appear, to put it mildly, “with a creak”. Many devices are updated with large delays, even more - do not receive firmware updates at all. At best, the support of the device ends approximately 2 years after its release, which promises one, maximum two system updates. However, people continue to use devices, which means developers have to focus on the (as yet) latest Android 8, and on Android 5, and even earlier versions, where there is definitely not only the latest, but even the current implementation of Java. For reference, now the total share of Android 7 and 8 - those versions

    Some may say that Java is outdated. But there is also an opinion that Java is a language that is “as is” good and should not be touched. It is like the gcd (the greatest common divisor) in mathematics. It contains a gentlemanly set of functions, and for those who need additional features, special languages ​​are already available that are already working on top of the JVM. There are already quite a lot of these languages: Scala, Clojure, JRuby, Jython, Groovy and others, less known. However, Kotlin has a number of advantages. This language leaves more freedom to the programmer: it allows you to simultaneously use ready-made fragments in Java, combining old and new code.

    But with all the advantages of the new language, which we do not in any way dispute, in the process of development there were some minuses in it. And today it would be interesting to hear the opinion of colleagues about whether they interfere with work in the same way as we do.

    Hide can not be shown?


    Packages, as usual, are a fairly common way to organize classes by namespace. But their plus is not only in this; in Java, they also act as a means to limit the visibility of classes and their members.

    Let me remind you that in Java there are 4 different categories that allow you to distinguish between visibility. For classes there are only two - they are visible either within the package (package private), or fully open (public). But a method or field can already be made private (it will not be available outside the class), visible only to the package (package private), it can be made so that the method is additionally visible to the heirs, both in its package and outside the package (protected), and it can also be made visible to all (public).

    In Java 9, it became possible to break code into modules, and now it is possible to make some of the code visible everywhere inside the module, but not from the outside. And it turns out to be very useful for building sensible APIs.

    In short, there are more than enough options in Java. But for some reason, Kotlin limited itself to introducing public and private visibility, as well as visibility for the heirs. In addition, however, they introduced a distinction between modules, but it became impossible to differentiate access to methods and classes by packages. A module is no longer a construction of the language itself, and quite often people understand this term as quite different things. Kotlin officially defines the module as a " set of Kotlin files compiled together ."

    The hitch consists that the module, as a rule, leads to additional expenses of resources. At the same time, if you create a separate package and put classes with functions that are visible only in this package, there are no problems, because all packages will be compiled together and no additional resources will be required.

    This is especially pronounced if, for example, to collect projects in Gradle, as usual, Android applications are collected. Modules tend to be relatively large, so that they represent a complete functional unit. Within one module, a method cannot be made visible to one and not visible to other classes. And if we want to make the visibility of methods more granular, a problem arises.

    Here I immediately want to remember about the packages, because in Kotlin this entity has not gone away, but, alas, it does not affect the visibility. Of course, you can always make more modules, but, given the characteristics of Gradle, it is simply irrational: the assembly speed will decrease. Yes, it is possible to thrust classes into one file, but in the case of a large project, the file will become really “heavy”. Therefore, I would like to get some other method of hiding methods, for example, following the Java model.

    Do not process it ... even if it is necessary


    The second one is quite controversial (because some consider their use as a mauveton), but nevertheless a minus is the absence of checked exceptions. These tools are in Java, but Kotlin decided not to implement them. The official documentation has an example about the Appendable interface. Strings can be attached to Appendable, and since strings can be connected to objects related to input / output, for example, when writing to a file or to a network, when you call interface methods, you can potentially get an IOException. And such cases make the use of checked exceptions inconvenient.

    The creators of the language explain their arguments as follows: if we use StringBuilder, accessing it via Appendable, we, it turns out, need to handle an IOException, even if you are sure that it cannot happen in principle:

    Appendable log = new StringBuilder();
    try {
        log.append(message);
    } catch (IOException e) {
        // Must be safe
    }

    The way out of the situation: the exception is “caught”, but they do nothing with it, which, of course, is not good. However, the question arises: if we deliberately work with StringBuilder through the Appendable interface - why not to interact directly with StringBuilder? There is an important difference: when working with Appendable, if we don’t know what kind of implementation it is under, I / O exclusion really becomes possible, but StringBuilder will not give it out exactly, and the corresponding methods are declared in it, although they implement the interface. So the example turns out to be rather strained ...

    It is particularly interesting that the authors of the documentation refer to chapter 77 in Effective Java, which says that exceptions cannot be caught and ignored. But only in the next chapters it is written that checked exceptions can and should be used if we do it wisely. If you quote so selectively, you can justify any point of view.

    As a result, the Kotlin developers made it so that, in effect, all methods could potentially throw some kind of exception. But then where are the tools for handling errors in the new language? How now to understand where the exception can appear? At the level of the language, alas, we do not find help in these matters, and even with the source code (which is not always available without taking into account various tweaks), it is difficult to understand what to do in each specific situation.

    If you ask a question to the developers, they just shrug their shoulders and say that in other languages ​​there are no checked exceptions and nothing , they also create large and reliable programs. But they are successfully written in frankly unsuccessful languages, this is not an argument. On StackOverflow, a similar question is said that “ you need to read the documentation ” is a good answer, very convenient in such situations. Only here the documentation may not be, it may be incomplete or outdated - the compiler does not check it. Finally, you can never find the answer in it.

    Yes, it is true that the presence of checked exceptions can lead to the creation of awkward APIs. When checking for exceptions is obviously unnecessary, so that the compiler is “satisfied”, you have to prescribe try ... catch and in fact just create a garbage code, because nothing is done while handling these exceptions. And the fact that in the functional code it is inconvenient to use them is also true. But after all, checked exceptions are only a tool that can also be used competently, wherever necessary.

    It is not clear why the language took this opportunity, if Kotlin's philosophy is to trust more tools to the programmer (at least, it seemed to us), and if the author of the code can competently describe where any exceptions will be, why not believe him? To take the same overloaded operators that were removed from Java, because they can lead to the appearance of API with unobvious actions - this was the protection of programmers from themselves. In contrast, Kotlin has the ability to overload operators and do many other things - so why aren't there any checked exceptions? Have the creators done it because it was just not like in Java?

    We are waiting for changes…


    The maximum that we can rely on for Android is Java 7 or Java 8 (but with some limitations and reservations), while Java 11 is on the way. Using Kotlin, Android programming becomes much easier, the number of lines of text is reduced .

    It remains to be hoped that the developers will complement this very useful language with those features that for obvious reasons are absent today. Maybe in the next versions there will be new tools for analyzing exceptions in the IDE, as well as new categories of privacy. But, apparently, this matter is at best a distant future, since the developers of the language have not even voiced any promises.

    Also popular now: