📜 ⬆️ ⬇️

From the Baltic Sea to the Indian Ocean

The story goes about how my journey began from the island of Kotlin to the island of Java.

It all started with the fact that the software revision in the organization in which I have the honor to work, showed the availability of various software for all needs, and as an option, I was offered to unify it, namely, to rewrite some of the projects in Java in order to keep the current functionality it was convenient. Yes, and interchangeability has not been canceled.

My share came from a desktop application for testing the main software. It is written in the promising Kotlin language (version 1.1 was at the time), but unification is unification. In this regard, I want to share a couple of moments that I encountered during the course of work, as well as the impressions that Kotlin made on me, since I was working with him for the first time. It is worth noting that there is quite a lot of information on this language, as well as its various comparisons with Java, I just want to share my impressions.

You should start with the fact that Kotlin is a good friend of Java, you can see that the creators of this language were guided, and the presence of the JVM bytecode compilation is also pleasing. This allows you to use different frameworks and Java libraries, and, if desired, to almost seamlessly cross-link these two languages ​​in one project, you just need to start by compiling the Kotlin classes. The project used TornadoFX and Exposed, the first is an analogue of JavaFX, and the second is a framework for working with databases.
')
The first feeling was, yes, this same Java only in profile, but with a lot of syntactic sugar, which allows you to write code faster and more compactly, but in some places it is much more complicated.

Looking at the code for the first time, you are slightly surprised by the abundance of "?", The absence of getters and setters, unchanged and changeable variables val and var, with automatic type inference or their presence, of course, if their initialization is postponed until later. Therefore, when analyzing the code, you often catch yourself thinking what object the colleague had in mind, whose inheritance I had to rewrite. Primitive data types, such as int, boolean, etc., are not observed in the language, as well as favorite ones from childhood “;” and “new”.

Let's start with the fact that the language is considered to be null-safe, as a result, in the code such constructions are often found:

Stand?.channell?.stream?.act() 

I really want to exclaim: "More questions for God questions!".
“?” Causes a slight bewilderment at first, it is clear that null-security is at the forefront, in this connection, every property of an object has to be checked for null, but why then leave the possibility of calling methods without checks for null, only replacing “ ?" on "!!".
As an example, the previous expression would look like

  Stand!!.channell!!.stream!!.act() 

The compiler will not check this expression. It will not check if a joint project is compiled with Java. The question is, what is the advantage then ?!

There are no checked exceptions in the language, and you will not see any IOException when reading the file. Good or bad to judge you.

Next, I want to share examples of the Kotlin code and its similar Java code to show how everything goes compact
KotlinJava
 private fun checkConnections(silently: Boolean = false): Boolean { var isNotFailed = true mqToClearTable.items.filter { isMqValid(it) }.forEach { mq -> if (mq.queue.isNotEmpty()) { isNotFailed = isNotFailed && checkMqConnection(MQContainer(mq.host, mq.port.toInt(), mq.channel, mq.queue, mq.manager), silently) } } return isNotFailed } 
 private Boolean checkConnections(Boolean tempSilently) { boolean silently = ObjectUtils.defaultIfNull(tempSilently, false); boolean isNotFailed = true; List<QueueToClearObservable> filteredQueue = mqToClearTable.getItems().stream().filter(this::isMqValid).collect(toList()); for (QueueToClearObservable mq : filteredQueue) { if (StringUtils.isNotEmpty(mq.getQueue())) { isNotFailed = isNotFailed && checkMqConnection(new MQContainer(mq.getHost(), mq.getPort(), mq.getChannel(), mq.getQueue(), mq.getManager()), silently); } } return isNotFailed; } 
 private fun armsTable() = armsTable.apply { columnResizePolicy = TableView.CONSTRAINED_RESIZE_POLICY items = currentStand?.arms?.observable() column("", ARM::hostProperty).useTextField(DefaultStringConverter())} 
 private void armsTable() { armsTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); if (currentStand != null) { if (CollectionUtils.isNotEmpty(currentStand.getArms())) { List<ARM> items = FXCollections.observableList(currentStand.getArms()); armsTable.getItems().addAll(arms); } } TableColumn<ARM, String> hostPropertyColumn = new TableColumn<>(""); hostPropertyColumn.setCellValueFactory(cell -> cell.getValue().hostPropertyProperty()); hostPropertyColumn.setCellFactory(TextFieldTableCell.forTableColumn()); 

The presence of default values ​​in call signatures in Kotlin is good news; in Java, such things have to be solved either through the Optional class or through reloading methods.
Yes, the functional style in Kotlin is currently worked out better, which allows you to add the necessary properties at the moment when the class object is initialized. For example,

  factory = new MQQueueConnectionFactory().applay { channel = mq.channel targetClientMatching = true hostname = mq.host.activ(); } 

In Java, however, we would have to organize several constructors, in which we could transfer the addition of these properties, or use additional methods after their initialization.

But there are, in my opinion, and controversial things:


A little more about the functional part of Kotlin. The presence of similar functions run, let, apply, also with different nuances of application, which causes dissonance when several such functions are used within the same construction using the keyword it (the implicit name of a single parameter), for example:

 testInfo.also { it.endDateProperty.onChange { it?.let { update(Tests.endDate, it) } } } 

You need to spend more time analyzing the code than is necessary to figure out exactly what this or that it belongs to, especially if the code is large and there is nesting of functions.

A couple of comments about the designers in Kotlin. Not only that, as with the methods, in the constructor you can set the default fields, the Singleton pattern is implemented at the language level, as in other, and data classes, unlike Java, where we again and again write all pens and several times. However, in my opinion, the implementation of inheritance is better organized precisely in the latter, where, unlike Kotlin, all classes are initially open, and when inheriting from them, we can safely redefine them. In Kotlin, however, a class or method must have an open mark in order to further override it. Also closed classes will not be able to proxy. There is also no implementation of static properties and methods in the language; instead, implementation is suggested through companion objects:

  class ExceptionHandler : Thread.UncaughtExceptionHandler { companion object { fun foo{ ….} val interval = 100 } } 

In conclusion, I want to note that Kotlin, in general, made a good impression. It allows you to write more simple and compact code, this is especially true when working with the GUI, but in industrial development, I still do not see it.

Source: https://habr.com/ru/post/352972/


All Articles