DEV Community

Kotlin - The Good, the Bad and the Ugly

Martin Häusler on September 21, 2018

Hi folks! This is an article I've wanted to write for quite some time now. I've seen my fair share of Kotlin and also used it in production. Kotli...
Collapse
 
lovis profile image
Lovis

Nice post, thanks for that!

I somehow understand how you got to your "bad/ugly" points, but it feels like it's written from a Java devs point of view.

But Kotlin is a programming language on its own and not a simple java substitute!

Here's my take:

Static functions

"This causes some annoyance in case you just want to define that one utility method"

I disagree. You can have top level functions (like you mentioned) or nicely scoped extension functions. Imho, you never really want a static function to begin with. It leads to procedural, rather than object-oriented, thinking.

open vs final

"If you write a class that potentially breaks when it is subclassed, you wrote a bad class."

I really think that this is impossible. Not even the original Java Developers were able to achieve that. Consider e.g java.util.Stack, which should most definitely have been final.
There's a reason that Effective Java Item 17. is "Design and document for inheritance or else prohibit it." and is not written the other way round. Also "Favor composition over inheritance" has been advice since forever for the same reasons.

By the way, I rarely run into problems with final vs open because I usually use interfaces and rarely even consider extending non-abstract classes.
The fact that the Spring Framework chose to work with subclassing is the Framework's problem, not the language's. That's why spring boot takes care of that for you by automatically applying the all-open compiler plugin.

constructors
I don't understand what your problem with constructors is. If the problem is that it's on the same line, then put it on a different line? Otherwise Java's got the same problem - the reason is too many parameters.

Data classes
I kinda agree with your data class opinion, but want to add that it might be a bad idea to use data classes for JPA since, JPA is... special. (vladmihalcea.com/how-to-implement-...)

Additionally, the keyword vs annotation thing is discussed here (blog.jetbrains.com/kotlin/2015/08/...)

Thanks for giving you thoughts! :-)

Collapse
 
cjbrooks12 profile image
Casey Brooks • Edited

But Kotlin is a programming language on its own and not a simple java substitute!

I could not agree more. When I picked up kotlin for the first time, I saw it as simply a replacement for Java, and did a lot of the things Lombok did. The more I use it, the more I've come to realize that I really do approach solving problems in Kotlin differently (and better) than I would in Java. In fact, new libraries I write in Kotlin, I'm trying to make sure they are multiplatform, so that I can't just treat it like a cleaner Java.

Collapse
 
martinhaeusler profile image
Martin Häusler

But Kotlin is a programming language on its own and not a simple java substitute!

I disagree on this one. The Kotlin language has been advertised by its creators from the very beginning to have excellent interoperability with Java. Therefore it should also play nicely with existing libraries and frameworks from the Java universe. My point is: the Kotlin devs must have seen those issues coming from miles away, and chose to deliberately ignore them, for the sake of forcing a design choice down people's throats (closed keyword) which isn't even that good in my opinion. Feel free to disagree, but to me this a big disadvantage of Kotlin.

JPA is... special.

Indeed it is. A lot more special than I would like it to be, honestly. There's so many things you can do wrong with JPA without even realizing that you made a mistake, until it is way too late to rectify it properly. It would have been nice to have a framework where you can model your data easily and have dedicated support to ease the mapping of those data models. Data classes would have been a good fit.

Additionally, the keyword vs annotation thing is discussed here

I've yet to read the entire article (thanks for linking it), but @inline doesn't seem so bad to me. A lot better (and less scary) than a magical "sometimes I'm a keyword" inline, in fact. The ability to use annotations without a leading @ that distinguishes them from keywords makes my neck hair stand up in terror. Good thing that didn't happen.

I don't understand what your problem with constructors is.

I like my code to be structured in blocks: declare the class name, declare the superclass, declare the fields, declare the constructor. This isn't possible with primary constructors. It's all mixed up into one Gordian Knot. Also, there's the lock-in effect when using primary constructors. You can't rely on a secondary constructor in a subclass. Overall, data classes aside, I simply don't see the need for primary constructors. They just increase the mental efforts required for parsing the source code of a class. Where are my fields? They are not listed... oh, right, they might be listed in the constructor... argh I like to have things organized, and find them always in the same place. Now I have to look at least in two places until I find all the fields of a class. It's mixing what doesn't belong together.

I rarely run into problems with final vs open because I usually use interfaces

Then you are not using frameworks such as Spring or JPA. I don't say that I like dynamic bytecode generation at runtime (quite the opposite...) but it is a hard fact that those are the primary frameworks we work with on the server side. Apparently, Spring 5 got dedicated support for Kotlin, I've yet to look into that, maybe they fixed this particular issue somehow. As stated above, the Kotlin devs must have seen this coming.

Imho, you never really want a static function to begin with.

I often do, because I like to have named constructors that take different arguments, so I create factory methods (e.g. User.createFromDTO(userDTO) instead of new UserImpl(userDTO)). The nice thing about these is that they can be placed directly into the interface (Java 8+), the code which uses it doesn't even have to be aware of UserImpl. If I wanted to do the same thing in Kotlin, I need a companion object that holds the factory method. The companion object construction as such (as stated in the blog post) is something that I feel wasn't necessary to begin with, and it has become a syntactic hurdle for cases that actually matter.

Just my 5 cents, as I said: feel free to disagree. If you use Kotlin in a different environment (e.g. Android), your experiences with Kotlin may vary drastically from mine.

Collapse
 
vishnuharidas profile image
Vishnu Haridas • Edited

Data classes are very useful when declaring POJOs for processing JSON, with some extensions. For example, I can have the below setup to fetch a JSON that contains a user's data and convert it into the App's user model to remember locally in my app:

data class UserResponse(id: String, email: String, name: String){
fun asAppUser() = AppUser(name, id, email) // Convert to App's model.
val isVerified: Boolean
get() = id.endsWith("-unverified") // for example, id="37823-unverified"
}

// Somewhere in code,
val jsonString = fetchUserDataFromServer()
val user = Gson().fromJson(jsonString, UserResponse::class)

if(user.isVerified){
rememberUser(user.asAppUser())
}

Collapse
 
kayis profile image
K

"Imagine frameworks such as Spring or Hibernate, which generate bytecode at runtime that extends your class."

Heavy use of code-generation is always a red flag to me.

Collapse
 
martinhaeusler profile image
Martin Häusler

I think we have to differentiate between source code generation and byte code generation here. Both are entirely different beasts with individual pros and cons. In this case we talk only about byte code generation. At the very least, this is code you will never see, which will never go out-of-sync with your source code (because it is generated at application startup).

Overall, I would strongly prefer to work entirely without code generation in any shape or form, byte or source. But in the Java server world, the hard truth is: pretty much any notable server-side framework (JPA, Spring, Spring Data...) has to generate bytecode to achieve its goals in one way or another. You can't avoid it. And usually it doesn't matter, because you never see it happening. As long as you don't declare your classes explicitly as final (which I hardly ever see people doing), you'll be fine. With Kotlin, it is just the other way around: declare your class as open, or face inexplicable exceptions at runtime. Like I said in another comment, the Kotlin language developers must have seen this coming, I can't imagine that this came as a surprise to them.

Collapse
 
defiance profile image
Defiance Black • Edited

I have no experiences with Kotlin but it interests me as a gateway into Android apps. Is there a short story to tell regarding Kotlin + Android specifically (and would you tell it)?

It seems understandable if the integration is butter smooth with very little story at all.

Collapse
 
cjbrooks12 profile image
Casey Brooks

I've worked with Kotlin/Android a good bit, and would highly, highly recommend it. It is especially useful considering that Android has Java 8 support for syntax only, but standard libraries (like the Streaming API) are only available on much newer versions of Android and so cannot be used because of backward compatibility reasons. Kotlin lets you do all the functional .map { }, .filter { }, etc. that you want, and it still compiles down to Java 6 bytecode to work perfectly on all Android versions.

They also have the Anko, Kotlin Android Extensions, and Android KTX libraries, which are all a treasure trove of useful extensions to make the Android APIs much nicer to work with.

Collapse
 
smartym profile image
Sergey V. • Edited

I agree with you. What's important, JetBrains is constantly improving Kotlin and working on extending its capabilities. New versions are being often released, and that's great.

As a mobile app engineer, I'm happy about the appearance of such an advanced language as Kotlin. In our company, we use it more than 50% of our Android development.

Null-safety, extension-functions, coroutines, JVM backend, JavaScript official support, and many other cool features make it an effective tool for writing apps. By the way, TechBeacon included Kotlin in the list of 5 emerging languages with a bright future.

Thread Thread
 
defiance profile image
Defiance Black

Is it a fair assumption that the rest of the 50% is essentially HTML/CSS/JS? While I do webdev, the appdev side of things is still cloudy; I'm unsure if there'd be anything else required.

Your comment makes me better appreciate my PyCharm subscription. When the time comes, it's all a few clicks away.

Collapse
 
defiance profile image
Defiance Black

Those links are all new to me; butter smooth Android integration indeed. Thank you, drive-by story-teller.

Collapse
 
bill1550 profile image
Bill Hart

I've been developing for Android now for the last 18 months exclusively with Kotlin. It is much more productive and safer than Java and is fully supported by Android. When working on older projects I find it is usually better to convert whatever classes I'm working to Kotlin. In particular the explicit and concise handling of nullable types is a major quality improvement.

Collapse
 
martinhaeusler profile image
Martin Häusler

I'm experiencing similar things on the server side. As I wrote in the article: null-safety alone should justify looking into Kotlin for every Java developer out there. It is a game-changer.

Collapse
 
vishnuharidas profile image
Vishnu Haridas

I strongly recommend Kotlin+Android. I am amazed with the better, functional style syntax, Extension Functions, Coroutines, Null-safety, and many other features. Threading and background processing is much easy with Coroutines.

Kotlinx - Kotlin Extensions is a collection of functions. For example, if you have a button in a layout XML named btnAccept, you don't need to do findViewById(R.id.btnAccept). Instead, you can directly use the name btnAccept inside your activity.

// example
btnAccept.setOnClickListener { callAcceptApi() }

Here's a nice list, #31DaysofKotlin, a full month of tips and tricks: twitter.com/i/moments/980488782406...

Collapse
 
martinhaeusler profile image
Martin Häusler

Sorry, I've got no experiences with android so far. But considering that Kotlin is an officially supported language on Android, you shouldn't face too many issues. I'm using kotlin on the server side, and it works like a charm alongside our existing Java code.

Collapse
 
defiance profile image
Defiance Black

Heard that.

I'll come back to this top-down view of Kotlin to refresh my mind on its concepts. You should have seen my jaw when I read about inheritance (Python has spoiled me). It's also a great mini-syntax tutorial.

Thank you for writing this thing!

Collapse
 
jagdeepsfo profile image
Jagdeep Sandhu

The kotlin drawbacks are not drawbacks if you want to use the functional programming paradigm. OO doesn't make sense in all domains, so classes and inheritance sometimes add to noise of the program. Kotlin data class is replacement for Python tuple. Since the JVM is not getting the investment that it used to get 20 years ago, I would really like to compile Kotlin to Native. In the age of docker container running a JVM makes little sense, it just adds another unnecessary dependency.

Collapse
 
martinhaeusler profile image
Martin Häusler • Edited

The kotlin drawbacks are not drawbacks if you want to use the functional programming paradigm. OO doesn't make sense in all domains, so classes and inheritance sometimes add to noise of the program.

Sure, you can do that. But then I would argue that you should go for Haskell or F#, because even if Kotlin has a lot of functional aspects to it, if you really want to go for it, then you might as well go all the way. The merit of Kotlin IMHO is that it supports both paradigms. It just falls short a little on both. For example, on the functional side Kotlin doesn't support Currying or partial application (without additional syntactical tricks). On the OO side, it doesn't support inheritance on data classes.

Kotlin data class is replacement for Python tuple.

I don't think of them as replacements for tuples, and honestly I've never heard th is argument before. They were called data classes by the Kotlin devs for a reason. They can have methods. They basically do everything a regular class does and more, except that they, for some weird reason which I really fail to see, can't use inheritance. Data classes would be a lot more useful if they could inherit at least from one other data class.

In the age of docker container running a JVM makes little sense, it just adds another unnecessary dependency.

I think that this point of view is a little bit on th extreme side. The JVM still gives you a lot of benefits, such as a garbage collector, all the profiling tools (JVisualVM etc.) and in general just a great infrastructure. Languages like Go are catching up, but slowly (different discussion entirely). I would welcome the possibility to compile my kotlin code to a native executable, or even to Go code.

Collapse
 
yuriykulikov profile image
Yuriy Kulikov

I would disagree about data classes and inheritance. There is absolutely no need in inheritance, especially with data classes.

Collapse
 
martinhaeusler profile image
Martin Häusler

This may depend on your domain. If you want to use data classes for Data Transfer Objects (which would make perfect sense because they are just data containers, or "structs" if you prefer this term), then inheritance is ordinary day-to-day business (at least in the domains I work with). The only alternative to the use of inheritance would be to duplicate fields into every leaf class, and remove the base classes. This makes the design brittle, because not only are you duplicating information (DRY principle), you also run into the issue that you might forget to add a field in one of your leaf classes in the future. I strongly believe that inheritance is necessary, and this is the reason why I don't use data classes for DTOs, as much as I would like to do so.

Collapse
 
cjbrooks12 profile image
Casey Brooks

Kotlin allows you to define properties in interfaces, and you can still implement interfaces in a data class. This frees you from the brittle design of duplicated properties (at least, in remembering to put them all in), and also solves the issue of wanting to use a data class as multiple types. I've tried this pattern out in my Dokka-to-JSON formatter and it works quite well, and is very robust.

I do not think the choice of data classes being final was an oversight. I think it was made very intentionally, in order to get you truly using data classes as pure data composed of other data. They are not in the same "class" of objects as normal objects, they are useful primarily for serialization, and its generally a good design to not have your code interact directly with a data class any way. You should abstract your code away from the structure of the serialized data into a format that works better with the business logic.

Thread Thread
 
martinhaeusler profile image
Martin Häusler

You should abstract your code away from the structure of the serialized data into a format that works better with the business logic.

Totally in agreement here. That's why a quick way for building serialization formats is a great benefit.

Kotlin allows you to define properties in interfaces, and you can still implement interfaces in a data class.

I must admit that I didn't think of this option yet. It would allow you to do all the inheritance you want on the interface level, and instantiate only the leaf classes. It also enforces the correct properties and types. Okay, granted, that seems reasonable. It does entail though that you need to write the interfaces and the data classes, and in a lot of cases there will be only one implementation for each interface (DTOs for example). Still, thanks for the heads-up. I'll consider this in the future.

Thread Thread
 
yuriykulikov profile image
Yuriy Kulikov

You can, of course, implement interfaces in data classes, but, in my opinion, compisition will always be a better way to do it. Want to avoid property duplication in your DTOs/value objects/whatever? Move the duplicated part to a new data class and use it as a property in another class. I have yet to see a use of inheritence which can be justified. Normally it is just a mess. Steer clear of it :-)

Collapse
 
cjbrooks12 profile image
Casey Brooks

About primary constructors, you definitely don't have to list all the properties in the class. Just the ones you want initialized in a constructor. I will agree that the syntax is a bit quirky, but overall I've found them to be quite helpful. I had adopted a pattern in java of always chaining overloaded constructors to a single, "root" constructor, passing the default values along the way, to ensure all objects are initialized the same way. Kotlin just enforces this pattern.

I also prefer a different syntax for declaring primary constructors, which makes it easier to read.

@ClassAnnotation
class ClassName
@PrimaryConstructorAnnotation
constructor(
        val1: String,
        val val2: Int,
        private val val3: Double
) : SuperClass(val1) {
...
}

This format makes it very clear which annotations go where (and I use @Inject on nearly all of my classes, so is quite necessary for me to have the constructor keyword even in the primary one), and also keeps properties each on their own lines, just like when declared in the class body.

Collapse
 
martinhaeusler profile image
Martin Häusler

Thanks for sharing your experience. This way of formatting improves the situation a little. However, for me it's still counter-intuitive. When I open up the source code of a class file, there are three things that I am primarily interested in:

  1. what kind of class is it? (regular class, interface, enum...)
  2. what's the name of the class?
  3. what is the base class, and what are the interfaces?

While the "Kotlin way" satisfies 1 and 2, it fails on 3. Look where the base class is located. Somewhere way down there. The very concept of declaring fields (which belong to the class) within a constructor is appalling to me. A constructor is a member of a class. Why should it contribute to the structure of the class (in terms of fields in this case)? I realize that this is not really the case, but the syntax makes it look as if that was happening. Maybe it's just because I've written so much Java and C#.

Collapse
 
mgroovy profile image
mgroovy

From what you say Groovy might be a language you should have a look at: Kotlin takes many of its Java improvements from Groovy, but Groovy avoids some imho strange syntax and design decisions, staying as close as possible to Java syntax and never unecessarily restricting the programmer.

Many mistake Groovy for a "script language", but as someone who was written a large framework (Vaadin/Ebean/Oracle/Tomcat technology stack) in it over the last years, I can safely vouch this not to be true: Groovy can be used as a dynamic as well as statically typed language (@CompileStatic/@CompileDynamic annotation on class or method), and comes with a multitude of powerful annotations (@Immutable/@Canonical/@ToString/@AutoFinal/etc) that make for very concise but readable programming. It has flow typing, closures (in addition to Java lambdas), a large extension library that enhances existing Java classes, a huge ecosystem - and is probably the most Java compatible language besides Java itself G-)

(For more details see arscreat.com/wprs/goltrta/ and on newer features groovy-lang.org/releasenotes/groov...)

Collapse
 
martinhaeusler profile image
Martin Häusler

We use groovy at my company too - but not for application development, but as an embedded scripting environment that allows the user to extend the application at runtime. It does a very good job at that.

If you remove all the meta-programming from Groovy and restrict yourself to the @CompileStatic subset, Groovy is a fairly decent language. My pet peeve with Groovy is that the language itself is far larger than the "safe" subset. I imagine it would be difficult to enforce the usage of this subset across the entire project and across all developers. Also, people are tempted to bring in external libraries, and those might make use of language features which do not conform to @CompileStatic. Basically, @CompileStatic divides the groovy world in two halves; and the 50% that does not use @CompileStatic I'd rather not deal with, at all, ever.

I've seen groovy both at its best (scripting with @CompileStatic) and at its worst (ever seen the mess that is gradle?). From all JVM aliens, I would personally rank Groovy second (behind Kotlin) for general purpose use, but ranked first for runtime scripting.

Collapse
 
mgroovy profile image
mgroovy

Hi Martin, could you elaborate on the "the language itself is far larger than the safe subset" and "remove all the meta-programming from Groovy" ? I don't quite get what you mean: Do you write your own AST transformations in Groovy in your project, or do you mean the built in AST transformations that come with the language (e.g. @Immutable) ?

Collapse
 
mgroovy profile image
mgroovy

In what areas is Kotlin better than Groovy in your opinion (apart from always being @CompileStatic ;-) ) ?

I have not used much Gradle myself, so I cannot really comment on it, I only know it is in widespread use. But so is make, of course ;-)

You might have noticed that our technology stack (which I as lead picked) is very @CompileStatic (Vaadin/Ebean), and does not e.g. use Grails or GORM. Having said that, I started using Groovy when @CompileStatic did not exist (and someone had just released a static Groovy compiler (groovypp), and approach that was rejected by the rest of the Groovy devs in favor of the more flexible @CompileStatic approach, which led to same guy later designing Kotlin), but due to the excellent IntelliJ* Intellisense support for Groovy I never really missed static compilation (probably a different story if you use Groovy as an embedded scripting language without Intellisense support, though).

(Only in the last 2 years have I started to shift larger portions of our code to use @CompileStatic , since IntelliJ sometimes makes refactoring errors on dynamic Groovy code. Also static compilation is helpful for less experienced developers and/or in areas where there is no time to have 100% test coverage.)

*Ironically the company behind IntelliJ, Jetbrains, of course also makes Kotlin. Alas it looks like the conflict of interests has started to show recently, when Groovy features were only supported in IntelliJ a year after they had been introduced. Besides Java, Groovy as the #2 JVM language is the main competitor of Kotlin. I would rather see the two sides join forces tbh, but that's not likely to happen...

Thread Thread
 
martinhaeusler profile image
Martin Häusler

Sorry for the delayed answer - busy times.

Kotlin enforces its type system everywhere. Every kotlin library out there adheres to it. The same cannot be said about Groovys @CompileStatic. Things like the JSON Slurper simply won't work under static compilation. Basically all the metaprogramming features contradict @CompileStatic.

I know, there are valid use cases for metaprogramming. But for general application development, I'd rather forfeit metaprogramming and get meaningful compilation results. In a way, it's nice that Groovy offers the choice, but it backfires when you need to include libraries which made a different choice than you did for your code. I don't want to deep-dive into a feature comparison of Kotlin vs. Groovy, but this fact alone (at least for me) would justify choosing Kotlin over Groovy for general application development. If you want an example of the extensive problems an optional type system can cause, you don't have to look any further than the current JavaScript/TypeScript mess. It's metaprogramming (JavaScript with monkeypatching) vs. well-defined strict type system (TypeScript) all over again. I'd rather stick to a language which doesn't even ask that question.

Collapse
 
newcolours profile image
NewColours

I'm in a minority here, but for me Kotlin use is a huge red flag.

The weakest developers use it to work around understand basic SOLID code and without understanding what it's going to generate.

The advantages you list like NPE safety are only truly advantageous in the hands of seasoned engineers. The rest of the time they are the reason weak devs recommend Kotlin to eachother in stackoverflow comments - to avoid learning to produce safe, efficient, quality code and to avoid learning concepts like nullability, inheritance and encapsulation. (The way they would previously have recommended static everything as a terrible solution)

Collapse
 
edwardvanraak profile image
Edward van Raak

So if a bridge would collapse you would blame the newly invented materials instead of the faulty design conceived by inexperienced engineers, who did not fully understand said materials?

Slavish devotion to a few general principles and the worship of acronyms is pretty stupid as well.

Collapse
 
rmirabelle profile image
Robert Mirabelle

Nicely written. I totally agree with your assessments.

I'm frustrated almost daily by Kotlin's EXTREMELY awkward constructor logic, and it gets worse when you factor in usage of the init block, which will be required if you actually need to do any work in the constructor. Primary, secondary, init...it's all a bit ridiculous. For the life of me, I cannot fathom what problem the Kotlin devs thought needed to be solved, but I feel confident stating this wasn't the right way to solve it. It feels like someone was showing off: "Look how much logic we can stuff into a single undecipherable line!" Brevity should follow clarity, not the other way around.

Data classes can't participate in inheritance, yet no one can ever infer that restriction. Instead, every Kotlin dev will be forced to discover it once their code fails to compile. Perhaps naming the class FinalSealedDataClass would have been helpful, but allowing inheritance would be better.

And possibly my least favorite language construct of all time (second only to Android's AsyncTask) is this: inline fun<reified T>. Dear lord, I had to google what the word reified even means. It basically means to 'make an abstract thing real', and while technically accurate, is a mightily poor cue for understanding that you're instructing the compiler to translate the generic T into a concrete type at every call site at runtime. And while we're at it, I think Kotlin should reify its own damn T's and inline generic functions as needed without me having to tell it to do so ;-)

And finally, coroutines - designed to make working with threads easier - and all that's required to use them is a complete understanding of absolutely everything about concurrency. Here again, I think the Kotlin devs have conflated simplicity with brevity.

These not-insignificant complaints aside, Kotlin is actually rather fabulous.

Collapse
 
leoat12 profile image
Leonardo Teteo

When I heard about Kotlin for the first time I heard that it was a language full of features that solved many of Java problems: verbosity, NPE, etc. People also said that it was a smooth learning curve for any Java developer, I thought it was nice and since the learning curve was smooth, maybe I could do what I liked to do more: jump into a language with examples and all that. It was not a very good experience...
It was some months ago, so some things may have changed and my memory can fail, but the first example I jump into was Spring Boot, since I use it every day in Java and it is already concise and easy to use, so even more concise would just make me love it more, right?
I created the project and started to write, reading the documentation when needed, but when I finished writing the Hello World example... Errors appeared...
More recent examples don't have this anymore, but at that time it was necessary to use a companion object to start the Spring Boot application, it was hard to find out that, and then there is lateinit for autowired properties, compatibility between Java libraries and Kotlin which made the use of my favorite libraries very troublesome. I remember a had a lot of trouble trying to figure out how to use Jackson. lol
Yeah, it was not smooth at all... I'm returning now since there are examples of projects in Kotlin in the book I'm reading. I think I will give it another chance.

Collapse
 
tbroyer profile image
Thomas Broyer

Wrt data classes, having inheritance would break the Liskov Substitution Principle wrt equals(), making things break as soon as you use such objects in sets or as map keys.

There's some discussion about this in errorprone.info/bugpattern/EqualsG...

If all you want is a DTO and don't really care about equals et al (or all the things you get for free with a data class, i.e. destructuring assignment and copy()), then just use a class, not a data class:

class Person(
  val firstName: String,
  val lastName: String
)

I'd say the biggest problem of data classes is that they're too often abused.

See also jakewharton.com/public-api-challen...

Collapse
 
androiddeveloperlb profile image
AndroidDeveloperLB • Edited

Some comments:
"Semicolons are optional" - in some cases they actually made it a must. Not sure why.

"NULL is a separate type" - this is good, but it doesn't protect you from a very nasty bug, that when you need to convert from Java to Kotlin, and the tool of the IDE doesn't really use the correct type. It causes a different exception...
The conversion tool is very dangerous. Need to handle it carefully.

"Flow Typing & Type Inference" - actually, this isn't always true. Many times, Kotlin insists that I tell it the type of the variable, even though it's clear what it is. For example, I can't use this:

data class Foo(val tt = 123) {
}

"Smart standard library" - About the sample code, this actually made me confused in the past, as "List" isn't quite the same on Java and Kotlin. And I don't think we really need MutableList.

"Streams, streamlined" - it is shorter, but it can be much less readable than normal code sometimes. I think it depends on the case. I wonder if it has advantage in terms of performance.

"No static modifier" - not only that, but only one "companion object" allowed, and if you call from Java, you might need to call ".Instance" unless you add the @JvmStatic annotation for each static function (because the converter doesn't add it for you). But, good news is that if you have a class that all of it is static stuff, you use "object".

"The open keyword" - So true. Really hate to have it this way. Ruins the entire openness of Android development. I've heard though that it's possible to overcome it somehow (without modifying the code of the final-class). Do you know how?

"Constructor Galore" - I actually like it. Your choice though. And when you need more, you change to a different way of writing it.

"Data Classes" - didn't know they are final. Didn't use them much though. Good to know. Made a request about this:
youtrack.jetbrains.com/issue/KT-27063

Collapse
 
martinhaeusler profile image
Martin Häusler

First of all, thanks for all the input!

"Semicolons are optional" - in some cases they actually made it a must. Not sure why.

The only case that I have ever encountered in Kotlin where a semicolon was actually required is when you have multiple statements on the same line of source code. Consider this:

// semicolon is actually required here
val x = 3; val y = 5

... however, you can always reformat your code to be one per statement:

// semicolon be gone!
val x = 3
val y = 5

I never encountered any other scenarios where the compiler wanted me to insert a semicolon. And honestly, having more than one statement per line is rather ugly and unnecessary.

The conversion tool is very dangerous. Need to handle it carefully.

I didn't want to open this pandora's box in the article, it's an entirely different discussion. But I agree with you: the tool, while conceptually nice, is indeed not without flaws. The tool is nice when you are learning Kotlin: you can write some Java code, throw the tool at it, and discover how it would look like in Kotlin. However, I would never trust it when it comes to production code.

Many times, Kotlin insists that I tell it the type of the variable, even though it's clear what it is

What you are trying to do in your code is to declare a field with a default value, and expect the compiler to infer the field type from that. While I do agree that this is fine for local variables, I would argue that the same logic does not necessarily apply to fields, because they have a much broader scope (and a longer lifetime). So if you say (in a constructor argument) val x = 3, then do you really want x to be an Int? Or maybe a Double? The compiler can't tell. Again, for local variables this is fine: if you discover that the type was too narrow (i.e. you actually wanted Double, not Int) then you simply go back and adapt your initial value assignment accordingly.

By the way, likewhise the Kotlin compiler doesn't provide type inference for function return values, even though there might be only one path through the control flow of the function which always returns a value of a certain type. But hey, let's be real here - it is still a big step upwards from what Java can do at the moment (Java is slowly catching up in this area though).

And I don't think we really need MutableList.

This is debatable of course. I personally really like it, because it saves me both the trouble and the performance loss for returning Collections.unmodifiablelist(myList) instead of just myList. It makes it clear to everybody which collections are safe to be modified, and which ones should be treated as read-only. I can see though that it is annoying to write Mutable all over the place. Then again, immutable data structures do have a lot of appeal, so maybe we should not use too many mutable collections to begin with.

But, good news is that if you have a class that all of it is static stuff, you use "object"

Oh okay, thanks for the heads up, didn't know that. I agree with you that the companion object was not the best idea they've ever had.

I've heard though that it's possible to overcome it somehow (without modifying the code of the final-class). Do you know how?

As it happens, I do :-) Fair warning: dangerous terrain ahead. You can actually write plug-ins to the Kotlin compiler (whether or not you should do that in practice is another discussion entirely). One such plugin is the All Open plugin, which is primarily required for using Kotlin together with the Spring framework (here's an article that highlights why this is so very much necessary), but the plugin works independently from any library or framework. What it does is that it basically implicitly declares all Kotlin classes as open. This takes us back to the way Java did it. However, note that it does not work on all classes. Data Classes, Sealed Classes etc. will still be final. Also note that with this plugin activated you have no way of declaring your classes explicitly as final anymore. Furthermore, I can't confirm how well the tooling (IDE) plays with such compiler plug-ins.

"Data Classes" - didn't know they are final. Didn't use them much though. Good to know. Made a request about this.

I appreciate the valiant effort, but I'm afraid you will not be greeted with a warm welcome for this request. I've seen more than one discussion thread online about this topic, and the Kotlin devs are quite stubborn about this decision. They claim that it is not possible to have inheritance for data classes because of technical reasons within the generated clone(...) utilities. As I stated in the article, I don't buy that argument. I sense plain old lazyness here.

Collapse
 
androiddeveloperlb profile image
AndroidDeveloperLB

About semicolons, incorrect. It is also required in enum classes (which I don't get why can't we just use "enum" instead of "enum class", BTW) that have extra code and not just values :

enum class SomeEnum{
    VALUE1,VALUE2;

    companion object {
        fun foo(valueInt: Int): Int = 123
    }
}

"...do you really want x to be an Int? Or maybe a Double? The compiler can't tell. "
Of course it can tell. It does it to fields (properties) and variables. What difference does it make if I put it directly in the CTOR...

About MutableList, I mostly just hate that they made it confusing. "List" should have been the same on Kotlin as on Java. Now it means a different thing. On Java you can do whatever you wish with it. On Kotlin you can't.

About making Kotlin open of classes, I mean something else. It was asked somewhere in Google (probably some Google IO lecture), and I think they said that if you insist, you can use Java to override Kotlin, but I didn't understand how.

Really on many (and probably most) things Kotlin did nice things, but on some I just ask myself "why did they do this? This is way worse than on Java...".

Collapse
 
kgtgit profile image
KgTgIT • Edited

You can use @JvmOverloads for constructors not to overload them by urself. But this is useful only for Spock and nullable fields.

As for data classes, you can use interface with a field. Then simply add override on field in data class. This may be useful for some code generalisation.

Collapse
 
krishisangaran profile image
krishi sangaran

Thanks for sharing this information. Nicely written. I totally agree with your assessments. I really like your blog post very much. Here I have drafted step by step tutorial on How to Develop Android Chat App Using Kotlin. It should be helpful for further process.
blog.mirrorfly.com/build-android-c...

Collapse
 
smartym profile image
Sergey V.

A great post about Kotlin!

Collapse
 
swingguy1024 profile image
Miguel Muñoz

Here's what I don't like about Kotlin. It's huge. This makes it hard to learn, and harder to maintain. One of the things I love about Java is that it's a small language, and it's very consistent in how things get done. They deliberately left out features like operator overloading, which C++ users found to be a huge pain in the buns, a source of bugs, and a maintenance headache. C++ is huge, and tried to be an "everything but the kitchen sink" (EBTKS) language, which is why a lot of C++ developers moved to Java. Now Kotlin is trying to be EBTKS, too, and it will cause headaches and will one day inspire people to move to some newer but not-yet-invented language.

The software world could really use a strongly-typed, rigorous language that was far less verbose and could lead to rapid development without giving up error-catching features (like strong typing). Dynamically typed languages have shown the demand for a good rapid-development language, but in my experience with them, version one goes very quickly, and version two is a huge headache, especially when it's done by a new team. I was hoping Kotlin would be that rapid-development, safe language that led to maintainable code, but I've grown very discouraged.

Collapse
 
eric_wilson_644bcce9ac4eb profile image
Eric Wilson

Just want to agree that static methods can be useful. One argument against them is in testing you cannot override them to return a fake value. But they are often convenient, especially as utility functions. But I could probably live without them okay.

Collapse
 
mohamedgara profile image
mohamed-gara • Edited

I like this article. Thanks for writing it.

Recently, I've seen this tweet twitter.com/odrotbohm/status/11611...

It's clear that Kotlin and Java integration has limitation and 100% interoperability is not true.

Collapse
 
eric_wilson_644bcce9ac4eb profile image
Eric Wilson

Great article! And some nice responses. It sounds like Kotlin was designed to make it harder to do what some people consider bad practices, such as inheritance and the use of static functions. Thank you for pointing that out. I have run into problems with inheritance in the past, so if it is harder in Kotlin, that is okay with me. Regarding defining fields in a constructor, to me that is not a problem, it is a time saving feature, similar to data classes. In TypeScript you can define fields in the constructor and it saves a lot of work. For example (TypeScript):
class TestClass {
constructor(private name: string) { }
}

I am new to Kotlin. Does it force you to initialize all fields in a constructor? If so, I think that is a good idea to prevent bugs.

Collapse
 
itsjzt profile image
Saurabh Sharma

Kotlin has a very large set of Keywords and Operators.

I really look for these things in languages. Feature rich languages are great but personally I like languages with small core.