
Kotlin Features I miss most in Java - curtis
https://blog.simon-wirtz.de/kotlin-features-miss-java/
======
lokedhs
I've been writing quite a bit of Kotlin, lack of semicolon isn't a great idea
in my opinion.

The problem is that while Java is newline-agnostic (newline is just another
whitespace), Kotlin has some rather difficult rules to determine the what a
newline does.

Granted, the rules are not quite as insane as those of Javascript, but I find
it very unsatisfying newlines are sometimes significant, and sometimes not.

And just to make it clear where I'm coming from on this topic, my preferred
language is Common Lisp, so I really prefer consistent syntax.

~~~
andybak
Yep. Consistency beats "syntactic noise". In fact lack of consistency _is_
noise. If semicolons are always present, then my brain filters them out. If
they are never present then great (yay! Python)

However - _occasionally_ present or absent semicolons means I have to think
about each situation - at least to a small degree.

~~~
morj239
The only place when you _really_ need semicolons Kotlin are the enum
constants, everything else can be considered "strictly no semicolons", so
pretty consistent.

------
thenewwazoo
This article wonderfully communicates the same feelings I have after writing
Rust, and having to write C/C++ for my day job. I regularly encounter
situations where I could do it clearer, safer, or clearer _and_ safer in Rust,
but instead I just return a null pointer.

~~~
Asooka
I have taken to, where possible, using something like this:

    
    
        template <class T>
        struct Optional {
          T value;
          bool hasValue;
          Optional():empty(false) {}
          Optional(const T& value):value(value), empty(true) {}
          // Also T move ctor, copy ctor, etc.
        };
    

And I try to make sure that all my structs' "empty" state can be set by
memset(0). Pointers are a bit tricky, especially strings, but for them I kind
of like this:

    
    
        class MyString {
          MyBuffer *buf; // Pointer to refcounted shared storage, i.e. CoW string
        public:
          const char *cStr() {
            if (!buf || !buf->chars) {
              return "";
            } else {
              return buf->chars;
            }
          }
        };
    

In short,

1\. Try to design structs such that they can be memset(0).

2\. Try not to return NULL pointers, if possible. Maybe keep static empty
instances to return instead of NULL pointers.

Of course, the overhead of that approach is not always acceptable, but when it
is, I find that it significantly simplifies the code that uses such classes.

~~~
thenewwazoo
Unfortunately for me, all of my C/C++ is in the embedded space (by that I
mean, I am _thrilled_ to do embedded programming. but it's C), so a lot of the
nicer features of C++ either aren't available to me (unsupported by the
compiler), or are too expensive to use (dynamic dispatch, exceptions). Your
Option implementation is definitely interesting; I may crib it some day. Half
my consternation comes from an itchy feeling of "why am I inventing this, when
Rust already does it!" that perpetually nags at me. It's entirely a hangup
that I make myself get over, but it's still there.

------
bunderbunder
It's funny, this article starts with my least favorite features of Kotlin -
ones that save a few keystrokes, but at the expense of readability.

And then it fails to mention so much of the very best stuff in Kotlin:
Extension methods (No, Java 8's default methods are _not_ a substitute.),
unifying the type system, covariance and contravariance instead of those wacky
wildcards, kicking checked exceptions to the curb. . .

------
Aardwolf
C++ features I miss in Java:

* unsigned ints. Even Java's 8 bit byte is signed??!

* operator overloading, mostly for math like matrices, vectors, complex numbers

* containers of primitives

* ability to write a "swap" function (pass by reference)

* compiler that turns readable code into good assembly (no verbose code needed due to simpler code introducing stray stringstream objects and things like that, ...)

* destructors that reliably do something at end of scope

* no need to write typename twice to create an object (altho C++ std lib on the other hand requires writing container name twice for begin and end for sort and other algos and has horrificly inconvenient number to string conversions so that part I don't miss)

* ...

~~~
peoplewindow
Signed-only ints is really painful when working with binary formats, for sure.
But the rules around unsigned vs signed ints in C/C++ are pretty painful.
-fwrapv shouldn't exist.

There is a library (GNU Trove) that provides specialised containers for
primitives. It'd be nice if the language had them built in, but they're only
quite rarely needed it seems. Normally there's some wrapper type involved.

Java has scoped 'destruction' (called try-with-resources).

An equivalent for 'auto' is coming to Java, though Kotlin has it already.

~~~
Rusky
> Java has scoped 'destruction' (called try-with-resources).

This is nicer than manually writing `finally` blocks, but it's nowhere near as
nice as RAII-style destructors, which you can't accidentally leave out.

~~~
pjmlp
Sure you can, you just need to allocate them on the heap and forget about
calling delete on them or not properly handle exceptions.

Not everyone is writing C++ as they should.

------
guelo
In the opposite direction, the main thing I miss in kotlin is Java's package-
private. Kotlin's internal is not really a replacement since it operates at
the module level which makes it more useful for library developers. Kotlin's
strict rules about not allowing tighter scope in the constructor of a looser
scoped class breaks my Dependency Injection pattern of injecting package-
private classes into the one public class in a package. Basically everything
in kotlin ends up being public.

------
donatj
I haven’t written more than a couple lines of Java in probably the last ten
years(I did maintain a small Swing app for a while before that) but to me a
lot of what he calls syntactic noise I call clarity. I would call having and
using multiple syntax’s to do the same thing is nothing more than noise that
hampers long term maintainability.

Many, maybe most large teams maintaining large code bases end up telling their
static analyzers to disallow this sort of thing for this exact reason.

Many ways to do something is easier to write, a single way to do something is
easier for future you to read.

------
myroon5
I wish comparisons to Java would use Lombok. I'm never going to actually write
something that verbose in Java.

~~~
stickfigure
I was going to write something similar. My takeaway from the article is that
Kotlin is perhaps a very minor improvement over Java + Lombok + some
functional libraries like Vavr.

This is actually my biggest complaint about Kotlin; it doesn't bring much to
the table that is very new. If I'm going to move on from Java I want something
more exciting like Ceylon's union & intersection types (which I get to use
in... Typescript).

~~~
s4vi0r
As a non-Java dev, Lombok seems like the kind of thing that could only be
tolerated by Stockholm syndrome java devs :/

I mean, it's a clever solution and the site/documentation is great. But
tossing around annotations everywhere feels hacky as hell, not to mention that
it only works with IDEs. I'm sure the combination you mentioned approaches
Kotlin in terms of features and QoL improvements, but it's just so much nicer
to have these things built into the language. Plus, Lombok is mostly
constrained to boilerplate reduction - Kotlin may not be as "crazy" as Scala,
but it still has some really nice modern features that java doesn't have/can't
have/won't have for a long time.

~~~
stickfigure
As a polyglot, I don't understand your problem with annotations. These really
aren't different:

    
    
        @Getter @Setter String foo;
    
        attr_accessor :foo
    

It's unfortunate that Java chose the silly capitalization convention for
properties instead of something more structured like C#'s get/set, but that's
really an aesthetic complaint.

------
TeMPOraL
Re "data classes" example in this article - Common Lisp features I miss most
in Java (besides macros):

\- multiple return values - basically doing what the author is doing here,
without introducing a new class for it,

\- destructuring - assigning pieces of composite object into several variables
(see e.g. destructuring-bind, or what pattern matching does in many
languages).

Basically, I often find myself wanting to write this:

    
    
      return {listWithResults, someBooleanFlag};
    

or

    
    
      return {listWithOneGroup, listWithOtherGroup};
    

and then use it like this:

    
    
      List<Foo> matchingResults;
      List<Foo> nonMatchingResults;
      boolean flag;
    
      {matchingResults, nonMatchingResults, flag} = functionReturningMultipleValues();
    

or:

    
    
      Foo first;
      Foo third;
      List<Foo> rest;
      {first, _, third | rest} = someList;
    

etc.

~~~
andrioni
If you want to stay in the JVM, Scala supports some pattern matching,
including your examples:

    
    
      def functionReturningMultipleValues() = (List(1), List(2), false)
      val (l1, l2, b) = functionReturningMultipleValues
    

evaluates to:

    
    
      l1: List[Int] = List(1)
      l2: List[Int] = List(2)
      b: Boolean = false
    

and

    
    
      val first :: _ :: third :: rest = List(1, 2, 3, 4, 5, 6)
    

to

    
    
      first: Int = 1
      third: Int = 3
      rest: List[Int] = List(4, 5, 6)
    

Of course, if you already are familiar with Common Lisp, Clojure might be a
better choice.

~~~
TeMPOraL
Thanks. Unfortunately, with the project I'm working on, I'm stuck with Java
the language - otherwise I'd have already replaced it three times with ABCL,
Clojure or Kotlin.

------
lanna
Someone should write "Scala features I miss most in Kotlin"

~~~
xaduha
No, that's not how it works. No one is forced to write Kotlin I don't think.
Plenty of people have to use Java.

The fact that Kotlin is a simpler language than Scala is a feature, not a bug.

~~~
wtetzner
Is Kotlin actually simpler than Scala? It seems to be less powerful in a lot
of ways, but on the other hand, it looks like a lot of Kotlin's features are
special-case things, whereas Scala's features are largely orthogonal and
composable.

I haven't really used Kotlin much, so I may be wrong, but that's the
impression I get.

~~~
andybak
> Is Kotlin actually simpler than Scala?

Unless Scala has radically been pared back in the couple of years since I read
about it or I was seriously misinformed at the time, then I'd say it probably
a yes.

Scala has the reputation for being at the complex end of the language
spectrum. Is that undeserved?

~~~
necrobrit
It's both... simultaneously.

The "feature set" really is smaller than Java or Kotlin (especially if you
count in terms of "keywords as features" and ignore things included for Java
compatibility).

The flexibility though can be genuinely overwhelming (the name, scala[bility],
refers to scalability of the syntax, not performance). Both in terms of the
syntax flexibility , e.g. where curly braces are optional vs not or different
ways of writing lambdas, and powerful/flexible features like implicit. However
there are very rarely special cases to these, it's all very consistent once
you know the rules.

Even the infamous underscore is "logically" consistent in it's use, it's just
a placeholder in both contexts you typically see it used (context of variables
and context of types).

Even my go-to example of a special case in scala, vararg unpacking:

`foo(myArgs: _*)`

Despite being a special case actually reads kind of consistently.. it reads
like a type ascription[1] asking for myArgs to be treated as a varargs of
whattever type. So consistent with other "type context" usages of underscore
despite otherwise being weird.

[1] [https://docs.scala-
lang.org/style/types.html#ascription](https://docs.scala-
lang.org/style/types.html#ascription)

~~~
oelang
> refers to scalability of the syntax

And the flexibility of the type system and the advanced type inference.

------
esfandia
Regarding the conciseness of lambdas and collections, why doesn't Java offer
methods such as map(), filter() etc directly in AbstractCollection? The
implementations would then delegate to stream internally, so no need for the
programmer to invoke stream(). I realize there are different kinds of streams,
but having a sequential stream as a default would be useful. That would be one
step closer to the Kotlin example.

~~~
dajohnson89
maybe backwards compatibility?

~~~
riku_iki
How exactly adding map() is not backwards compatible?..

~~~
xendo
Adding methods to non-final classes is always incompatoble. If you had your
own implementation of list and its interface suddenly requires new method, how
should it behave?

~~~
riku_iki
map() in AbstractCollection will obviously have some default implementation,
which will be used by all child classes.

------
lazaroclapp
Note that at least one of the features described, (static) Null-Safety, can be
achieved in Java via annotation processors. For example, by a tool such as
NullAway
([https://github.com/uber/NullAway](https://github.com/uber/NullAway)). Yes,
adding '@Nullable' is more verbose than '?' (thus is the Java way :) ). But
there are plenty of projects were it still makes sense to use Java over
Kotlin. This is particularly true for existing codebases, where adding
annotations is certainly easier than rewriting everything in a different
language.

Disclaimer: I am a contributor to NullAway. There are plenty of other
nullability static analysis tools for Java worth considering (Checker
Framework, Eradicate, etc). NullAway's main strengths are a focus on
performance and the idea that you should be able to use it as part of every
(local) build of your code.

~~~
SureshG
Kotlin has the best java interop for any of the JVM languages, so you don't
have to rewrite everything in kotlin . You can gradually migrate while using
the same tooling.

~~~
cesarb
And related to the parent comment: Kotlin's compiler understands several kinds
of @Nullable/@NotNull/@Nonnull annotations, and treats them as Kotlin's
nullable/not nullable ("@Nullable String" is treated as String?, for
instance). In the other direction, Kotlin's nullable/not nullable is visible
to the Java side as one of these sets of annotations (IntelliJ's IIRC), which
allows for instance annotation processors like Dagger 2 to know when something
can be null or not.

------
minus7
Oh Vert.x, I despise it. We jumped on that in 2014 (Vert.x 2) and it's still
haunting us. Half-broken build with an outdated Gradle plugin (should've stuck
with the official build system), doesn't integrate with the major part of the
Java ecosystem due to being callback-based (exceptions can hardly be used, nor
can anything that blocks on IO) and tooling for managing tasks doesn't exist
either (e.g. running a couple of IO-tasks in order requires you to nest the
callbacks if you don't write your own tooling).

I assume Vert.x 3 is doing a better job there, but the fundamental problem
remains: callbacks don't work well with exceptions. Maybe Kotlin helps
wrapping this nicer, I don't know. All I know is that I'll be more careful
when selecting a framework for productive use now. And maybe that I won't be
using Vert.x again.

~~~
zbentley
As a Node.js dev: don't hold your breath waiting for better exception support
in callback-based asynchronous code. Node didn't really have a procedural
legacy; it was always async (sorta), and exceptions are _awful_ to try to
reason about with callbacks. Then we got Promises, which made it worse. Now we
have async/await, which helps restore typical exception behavior a bit . . .
until you have to integrate with any code written in a callback- or promise-
oriented style (anything older than 15 months, almost all of the standard
library).

In general, I think without language-level support for continuations
(callbacks, coroutines, promises, async-"colored" functions, whatever),
projects like Vert.x are going to cause a lot of friction with regards to
exceptions. Having a large synchronous legacy to contend with doesn't help
either.

~~~
thenewwazoo
I don't mean this to be flippant: there's a Javascript standard library?

~~~
Too
Its small but there is one. Thats where you would find stuff like Math,
string, json, promise.

------
fnl
Nice write-up of the pain points in Java. Though, aggregating and mapping over
(in Java or not) (possibly infinite) streams is not really the same as doing
that over containers. In other words, Java8 doesn’t have the ability to
flatMap et al. an arbitrary container, unlike Kotlin or Scala.

------
vbezhenar
Honestly I don't understand point of data classes. Why was it introduced into
language? The only time when I'm implementing equals/hashCode is when I'm
going to use this class as a hash key and it's quite a rare situation. Why
introduce a keyword just for that? Am I missing something? I'm using Kotlin
and I'm just using classes without data modifier, they work just fine.

~~~
cle
It's really easy to throw a data obj into a map/set and forget to check that
it correctly implements equals+hashCode. Or add a field to a data class and
forget to update equals/hashCode. Or log a list/set/map without realizing that
the toString is just going to print some useless hash code. Data classes
handle all of that for you, automatically.

------
agumonkey
I tried kotlin again but personally (I would surely take it any day at work)
it's in a bad spot. It makes me miss python cuteness (zero start time and
dynamicity) too much, so if I need functional on JVM thing, I'll end up
writing some clojure.

------
wisecoder
Mostly looks like C# syntax to me

------
foxyv
My favorite is the null checking. The '?.' operator is GOLD.

------
nikolay
Is there any benefit of Kotlin vs C# except JVM support? Why not work on a
great C# to JVM compiler instead of inventing a brand new language?

------
morecoffee
Java is commonly criticized for being too verbose, but I never understood why.
I agree it is verbose, but verbosity isn't really that bad of a problem.
Programs aren't harder to write, they aren't more bug prone, and they aren't
harder to understand. On the contrary, high verbosity means less information
per token, making it easier in my opinion to read.

A good example is the anonymous class. They are portly and wasteful of
vertical space, often costing 5 lines when one would due. But, the amount
thinking per line is greatly reduced. In order to visually afford anonymous
classes, logic must be simplified or structured, commonly by splitting up a
larger function in order to fit it on a single screen. An arguable misfeature
of the language has a convenient side effect of forcing best practices.

~~~
syncopatience
Couldn't this same reasoning be used to argue that we don't need for loops or
functions because we have GOTOs? Boilerplate is tedious and error-prone to
write and to read/understand in my experience.

~~~
morecoffee
It cannot. The syntax bloat forces structural cleanliness. A lower level
problem is addressed with a higher level fix. It would be better of course to
not have bloated syntax, but not at the cost of better program logic.

As for tedium: Java programmers rely heavily on IDEs to "write" most of the
boilerplate for them. The IDE fills in tokens automatically, and hides them
visually with +/\- boxes to the side.

~~~
charleslmunger
But if the bloat is hidden, how is it forcing structural cleanliness?

~~~
morecoffee
Personally I don't hide it, and I do type each token by hand. Since the parent
brought up tedium, I wanted to mention that there are solutions to it. IDEs
hide bloat, but code review and other tools often don't.

------
arikrak
It seems like Kotlin is similar to Dart in many ways. New languages like them
are able to be more concise while Java is stuck staying compatible with its
past.

