Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Kotlin Features I miss most in Java (simon-wirtz.de)
147 points by curtis on Oct 29, 2017 | hide | past | favorite | 131 comments


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.


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.


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


I write Kotlin for more than a year and semicolon usage is pretty consistent: no semicolons. The only place I remember using them is in enum constants. I also came from the lisp world (CL -> Clojure).


Your second line, if true, makes your first line wrong.


Well only if you are a mathematician living in a box but this is the real world you know.


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.


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.


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.


You could use boost::optional. Or std::optional, come c++17.


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. . .


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)

* ...


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.


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

Java's try-with-resources (and Kotlin's equivalent, .use()) are a start, but C++'s (and Rust's) ownership model is stronger.

Java's model works fine when the resource doesn't have to escape the try block (and when you never forget to use the try block). But if you have a resource which might or might not escape the block (that is, its ownership might be to be passed to the caller, or to a separate long-living object), you have to do it manually, and if you're not very careful, you can have places where a stray exception leads to a resource leak. (Yes, this bit us recently at work, both the stray exception and the "another programmer didn't call .close()". Workaround for the later: finalize()...)

In "modern" C++ style (which avoids manual new/delete most of the time, instead using unique_ptr and similar), ownership transfer is easier to make reliable: you can move the ownership into the return value or into another object, without leaving any gaps where a stray exception could leak the resource. And the default is to have ownership, so less chance of leaks due to forgetting to use a try block. Rust makes it even easier to avoid such leaks (outside of reference cycles, unsafe code using raw pointers, and the "leak it on purpose" mem::forget function).


> 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.


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.


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

They took out operator overloading deliberately when they designed Java because in practice people mostly use it to obscure what's happening instead of making things more clear.


In C++ operator overloading was used by the standard library to obscure everything. I can see why Java took the direction that it did. But, on the other hand, we've learned from those mistakes and pretty much any new language that has operating overloading it's used both appropriately and sparingly (C#, Python, etc).

It's one of those things that as a developer you'd never use yourself but when you need them they're extremely handy. I'd loved doing matrix math in C++ -- I could translate complex mathematical expressions directly into code with almost no translation.


I agree it's a valuable feature if used in the right places. The problem is when you get a C++ job with an existing code base, particularly one which has been maintained by a lot of people over many years, invariably you're going to find operator overloading everywhere.


This must literally have been the list used at Microsoft when making C#. Well most of them at least.


I was wondering how many of these points are answered in C# (and in which version they were "fixed")


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

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

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

These were in from the beginning

> * containers of primitives

Generics (in the proper sense, not the Java sense) came in C# 2.0/.NET 2.0 in 2006. With those you can make a List<T> and have it backed by an array of primitives that is consecutive in memory. That is, if you make a List<int> it's not a list of some boxed Integer type.

> * compiler that turns readable code into good assembly (no verbose code needed due to simpler

A bit subjective, but I'll argue that the JIT is getting better and is still nowhere near as good as an ahead-of-time compiler that can spend time optimizing the code. The JIT has to be FAST first and foremost since it produces its assembly WHILE the code is already running.

> * destructors that reliably do something at end of scope

There are no destructors and the only deterministic destruction is disposal (via IDisposable). Objects are not freed at that point however. You only use it to free resources, such as deterministically closing a file. The managed object representing the file will sit in memory until the next GC.

The very bleeding edge version of C# yet to be finished will contain a lot more low level C++ like features which will make it more ergonommical to use stack memory (Google "Span<T>"). That will approach some of the C++ use cases.

> no need to write typename twice to create an object

Locals can be implicitly typed. So

    var pet = new Dog();
Is an allowed statement inside a method. This came in C#3 in 2007.


My favorite feature of .Net is probably expressions trees. (https://blogs.msdn.microsoft.com/charlie/2008/01/31/expressi...).

They have been around since 2008.

A lot of non .Net people think that expression trees are just lambdas, but they are so much more powerful.

If you have a method that has a signature:

Find(Expression<Func<Employee,bool>> query) {…}

And you call it by

foo.Find(e => e.id == 5);

That expression is treated as “data” not code. Meaning if your Find command is searching for Employees in SQL Server using Entity Framework, it will be translated to sql that runs on the server. If you’re using Mongo, it will be translated into MongoQuery. When you are unit testing you can mock it out, and process any given query against an in memory List<People>.

If you are crazy enough, you can even move it out into a micro service and parse the expression tree yourself into a query object that you send to an API.


Code quotations is similar in F#, but if you add F#'s type providers you can reach an entirely new level of crazy.


I would agree with plenty of those, but they are mostly not needed for the work Kotlin targets (ie Android and Web dev).


"* destructors that reliably do something at end of scope"

That's almost a reason for going back to C++ (or PHP) for me. C# doesn't have it either but interestingly managed C++ does.


C# does have scoped destructors, but for GC reasons they use a separate syntax. `IDisposable` and the `using() { }` block do this.

The downside is it's entirely opt-in, but it's well known and things like IoC containers can handle it.


Two issues with using is that

1. Its restricted to functions, you cant have an IDisposable member without having to implement IDisposable yourself and there manually close the member. Just allowing an annotation on the variable would have made this so much more ergonomic.

2. The lack of ownership semantics when passing objects as function arguments makes it hard to reason about the program. There is no difference between temporary borrowed reference, ownership transfer and shared ownership so you never know if you are the one that should call close or if one of the functions did or will do in the future.


Personally I find the "using" syntax ugly especially with many variables and as you mention it relies on the caller to do the right thing. It would be better if the author of the class could control the behavior better.


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

Kotlin has infix functions which help there without treading the dangerous territory of ambiguousness


Kotlin also has operator overloading.


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.


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.


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


Lombok addresses one pain point of Java (constructors/getters/setters), Kotlin addresses more. Lombok also does a bit of magic, so should be used with care. E.g. @AllArgsConstructor is a dangerous one if you change the order of fields, especially if you don't have IDE support for Lombok.


Not sure where the danger is with @AllArgsConstructor. If you change the order of fields, it's the same as if you changed the order of the constructor parameters. You'll get compile errors and be forced to fix them (IDE or not). Seems as safe as anything to me.


That assumes your constructor parameters are all unique types.


I would consider that a bit of a design smell, but yes. I think the danger point has more to do with training new converts: it is very unusual in any language that changing the order of class internals can change its external behavior.


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).


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.


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.


I think this is the case with most JVM languages, to be honest. As someone who has done some Java but would never do it willingly as part of a hobby project or the like I think Kotlin and pretty much every other JVM language is a huge step up from Java.

When you're already knee deep in it I suppose you're bound to have a less enticing reason to use the other languages.

That being said, I don't see how you can look at the feature list of Kotlin and play around with it without seeing how it's obviously a much better language.


> ...not to mention that it only works with IDEs.

That's not true. Lombok is an annotation preprocessor. That means it is running with javac which is independent of any build tools or IDEs. As long as it is on the classpath when compiling (-cp flag of javac) it works.

Of course for code completion you need an editor that is aware of Lombok / uses the compiled bytecode for that.


There's rarely a need for a language with anything "new".

There's a strong need for a language that brings the best ideas recently developed and puts them together in a new language that does not have baggage from the past, (e.g. D).

I've never used Lombok or any other code generator with Java.

I think the important features of Kotlin are:

- Null checking

- Proper lambda/function types

- Not everything has to be a class

- You can specify parameter name when passing to functions: specially useful when constructing objects.

- Simplified constructors (member fields can be specified in the argument list).

These were some of my biggest pet peeves with Java.

That, and the weird stream APIs.


Pitfalls of Lombok aside, I agree that it is indispensable to reduce the amount of boilerplate in Java. That being said, it is largely a hack but most of the IDEs have Lombok support so it's not _as_ bad.


AutoValue beats Lombok hands down. Not least because it is pure Java so no extra IDE support is needed.


Using Lombok is just as much effort as adopting another JVM language (you still have to teach all your tools about Lombok, adapt all your processes, teach anyone you hire...), for a smaller reward, IME.

(I used to use Lombok, the checkers framework, and various other pieces. I found Scala gave me all of that and more, for a similar level of effort)


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.


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.


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.


Yep, but it is a baroque abomination which I would rather steer clear of.


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


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.


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.


> Is Kotlin actually simpler than Scala?

Java was described as "blue-collar language". I probably won't call Scala that, but it fits Kotlin fine in my opinion.

another comment on that subject

https://news.ycombinator.com/item?id=15298075


> 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?


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


> refers to scalability of the syntax

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


The reputation is undeserved as a description of the language proper; it's a flexible and powerful language that allows libraries to do quite a lot, so people often mistake library features for language features. In a way it ends up being a bit like lisp: a toolkit for allowing you to construct your own (hopefully domain-appropriate) language rather than a language as such. Like how people will sometimes criticise Ruby for things that turn out to be a Rails feature rather than part of the language itself.

It is fair to say that many Scala codebases are complex and difficult to work on. It's a language that punishes poor choice of libraries much harder than many languages (particularly if people are used to JVM languages - what libraries/frameworks can do in Scala is much closer to what they can do in Python/Ruby/...). I don't think it's "fair" to compare Scala to Kotlin on those grounds, simply because we haven't had time for generations of Kotlin frameworks to build up. But obviously that kind of fairness is not your concern if you're a business making a choice of tools.

If you're choosing a language for a short-lived codebase then Kotlin might be a good choice if it does what you want. What I would say is, if you're trying to choose a language for a codebase that will live for 5-10 years, try to think about what Kotlin is going to look like in 5-10 years. Right now Kotlin is able to be a lot cleaner than Scala (at least on a superficial level) because it doesn't have backwards compatibility with large existing codebases hanging over it. That isn't always going to be the case.


It's simpler for a compiler at least


Kotlin is an officially supported Android development language. I'm sure some people get paid to work with it


Let's wait until a smug haskeller one-ups you with language snubbery.


Someone could just as easily create a similar list of "Scala features I'm glad I don't have to deal with in Kotlin."


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.


Why in AbstractCollection and not in Collection interface as a default method?

Apart from that I still miss the fact that Iterable doesn't have a `stream()` method, it would make it much more useful in post-java8 world.


maybe backwards compatibility?


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


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?


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


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). 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.


There's a huge advantage to having language level support for null safety. Annotations are just metadata, and managing semantics of that metadata is pushed to the programmer: to configure a static analyzer to act on them, to figure out the differences between different backends, make sure it's enabled in the build, write/install IDE plugins, etc. Language level support for this stuff means programmers don't have to worry about any of that.

Pragmatics of course necessitate that we sometimes need to tack these static analyzers on after the fact. But over time, the amount of tack ons and bandaids and unnecessary backwards-compatibility-necessitated complexity becomes so high that you're better off using a better language, if you and your team can handle it. For many people, this has already happened with Java. I kind of wish Java the language would just die already and stop adding just enough features to keep people from moving on to the next generation of languages. Science progresses one death at a time.


> Annotations are just metadata, and managing semantics of that metadata is pushed to the programmer: to configure a static analyzer to act on them, to figure out the differences between different backends, make sure it's enabled in the build, write/install IDE plugins, etc.

Sure, but keep in mind switching to a new language also entails a huge investment in tooling. There is definitely a higher cost in switching your build infrastructure, tools and IDE plugins to support a new language (nevermind porting an existing codebase) than the few configuration lines it takes to add an annotation processor. At a certain scale, the tools you need might not have even been built for Kotlin, while they have existed for years for Java.

> For many people, this has already happened with Java.

Yes. And, for many others, it hasn't.

Look, I am not arguing that we should all be writing Java until the heat-death of the universe. Whether it is Kotlin or not, I am sure better ways to do things will pop up. But there are good reasons to start even many kinds of new projects in Java today: maturity/efficiency of the compiler, tooling support (including static analyzers, since nullability type systems are not the only feature you'd ever want to bolt onto a language), etc. Over time those reasons will be less, but even then, existing Java codebases will remain, and it will still be worth making them safer. Is not like C, C++ or even Fortran codebases are not around anymore.


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.


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.


Interested in how you think Kotlin interop is better than Groovy? Curious because that to me is one of the main things that makes me choose Groovy in some situations over other languages (eg: Scala, Clojure, Jython, etc) where the impedance mismatch is much higher.


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.


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.


Actually it can work quite well without language-level support. Python's gevent library [0] shows that. It "simply" monkey-patches the standard library's IO functions to implicitly switch to another "greenlet" when blocking IO is performed. On the user side you don't really see anything of that (no "await" or similar). Admittedly, that hides possible points of context switches, but is still far less dangerous with regard to race conditions than OS threads. Unfortunately, gevent seems to be shunned quite a bit and I haven't seen other languages do similar things.

[0] http://www.gevent.org/


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


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


Node has more of one than your browser.


Kotlin coroutines support in Vert.x 3.5 allows you to write "regular" exception handling code inside of a method, it's nice.

http://vertx.io/blog/vert-x-3-5-0-released/#kotlin-coroutine...


Oh, lovely. I'll have to check out Kotlin's coroutines in more detail. So far I haven't seen many coroutine/green thread implementations I like. In fact the only one I like is Python's gevent since it's very non-intrusive and often works with existing libraries without modification, whereas Python's async/await or C# (I think) require every part to be aware of the coro (and unwind the stack when suspending)


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.


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.


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.


It may just be me, but I find that sooner or later I need to debug or print something to the console and having a reasonably to string method already implemented means I don't have to be annoyed I forgot it and go back.


1- automatic equals & hashcode is awesome. With data classes, you no longer need AutoValue.

2- it also adds toString and destructuring functions.

for the data layer, 'data' classes are awesome IMO.


equals/hashCode is useful in tests also when you want the check that the returned object is what you expect, instead of doing field by field comparison:

    Foo foo = bar.foo();
    Assertions.assertThat(foo).equalTo(new Foo(1, 2, 3));
instead of:

    Foo foo = bar.foo();
    Assertions.assertThat(foo.getFirst()).equalTo(1);
    Assertions.assertThat(foo.getSecond()).equalTo(2);
    Assertions.assertThat(foo.getThird()).equalTo(3);


One important reason for equals/hashCode on data objects is that you can put them into sets, and have them uniqued correctly.

Also, do you really never need to call .equals on data objects?


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.


Mostly looks like C# syntax to me


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


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?


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.


> On the contrary, high verbosity means less information per token, making it easier in my opinion to read

See I think this is actually backwards thinking. The more boilerplate you have to sift through, the more you're trying to retain before getting to what the program is actually doing.

> 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

Same applies for this statement. You're trying to absorb so much more code before knowing what is actually happening.


Yeah but the boilerplate is basically invisible to anyone with experience in the language


This phenomenon is a genuine quality problem.

Small bugs in the implementation of boilerplate code have a tendency to sail through code reviews and go without notice until they become production issues, because experienced developers develop a habit of glossing over boilerplate without reading it too carefully.


I think that's the issue though. One should not need to have much experience in any particular language to read and understand what a particular piece of code is doing. It should be relatively intuitive. The more boilerplate a language has, the larger the cognitive load. This holds true regardless of whether or not it is invisible to anyone, it's just that much more annoying. I'm not doing a good job of expressing my thoughts here, but I absolutely "feel" this way when working with Java vs something like Python.

That being said, I'm a big of Java and the eco-system.


"One should not need to have much experience in any particular language to read and understand what a particular piece of code is doing."

I'm not sure I agree with this. I think every effort should be made to keep your code readable to others with similar knowledge. However, I don't think you need to make everything readable by a junior level coder.

Maybe a decent analogy is that not all books target a 4th grade reading level. The varience of information density isn't just about saving time for those that understand it, but also for keeping you interested (or disinterested if you aren't ready for it yet). For example, reading young adult fiction in your 30s usually wouldn't keep you engrossed.


The older and more experienced you get, the harder it is to assume others who will have to deal with the code will have "similar knowledge".

"However, I don't think you need to make everything readable by a junior level coder." I may not be a junior-level coder, but I may still be 'jr' with your particular language or framework, but have been assigned your code because you've moved on to something else.


I've never written a nontrivial Java program but generally speaking I find it pretty easy to read Java code.


Exactly which is why no one is going to look at all those getters and setters you added not realizing that one of the setters actually does something other than setting the field or a getter causes some side effect before returning the value.


To be fair, most people use IDEs that generate them based on the fields so this would be unlikely.


Unlikely, but to be sure, you have to read all the boilerplate, attentively.

If your language generates them behind the scenes, so that nobody can modify them, that doesn’t happen.

Also, if a developer overrides one particular example, it stands out in the source, as it (ideally) is the only code visible in the source.


Unlikely edge cases are the biggest source of bugs. One of the worst production bugs I've seen happened because someone refactored some code assuming a getter just got the field, like 99.9% of getters do, but this particular getter didn't.


I think you miss my point. Most people DO generate them via an IDE so they don't get looked at closely. But imagine someone then editing one of the generated methods to do something different than expected. Happens all the time.


True, but it does have impacts, like you can see less of the overall program on the screen at a time, so to scan through requires more of a look->scroll->look->scroll.


Sure, but excessively dense code has its own drawbacks. Does anyone like reading regexes?


Compared to the equivalent handwritten state machine, yes.


I don't think dense code has drawbacks; people measure in lines of code because they implicitly assume all lines are equal, but that's completely wrong. Reading the same logic when it's 20 lines is much, much easier than reading the same logic when it's 100 lines, because whether you can fit it on a single screen or not makes a huge difference to whether you can comprehend it.


Never worked in Perl, I take it


I've worked in/with Perl. Perl is unpleasant because it's inconsistent, not because it's dense - if you want the extreme examples, compare Perl with something like APL.


Even if that were true, it still wastes a lot of space and can make reading the whole body of code more difficult. Just think about how easy it would be if Java had 2 or 5 times the boilerplate code it has now. According to you it'd be "invisible," but it'd still be a lot worse.


Ergonomics changes your design decisions. When some idiom of code or data or both is awkward to express, you'll lean naturally to something else, which may have drawbacks not related to ergonomics, like coupling or over-abstraction or duplication, etc.

Ergonomics goes into the mix of tradeoffs for design. Something like LINQ's expression trees, for example; not easy to do in Java not because ASTs can't be constructed, but because their construction isn't as ergonomic.


I think in Java this is most notable in the boilerplate it takes to create value types (and related: typedefs). This often leaks from the realm of bad ergonomics into the realm of bad design; primitive proliferation is a common problem that I believe would be less pronounced were there a more convenient way to define a value type.


This sounds like Stockholm Syndrome. The low-information verbosity is actually good because...


I've programmed in python, scala, and java quite extensively. There definitely is such a thing as a good writing language and a good reading language.

Python fits nicely in both, but java is a great reading language. If I had to maintain any existing codebase I would prefer a java one.

Scala is the great obfusciator.


I have used all 3 extensively, too. And when it comes to large, sloppy code bases, I'd take the Java over the Python any day.


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.


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.


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


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.


No, those things aren't even boilerplate.


I agree. Java is one of my favorite languages partly due to its verbosity. It's easy to figure out what I was doing when looking at my code later on. It's also easier to repurpose old code.


There is a language agnostic rule hat there is a linear relationship between lines of code written and number of bugs.

High verbosity == more lines == more bugs.


Depends on the person I guess. I like terse code. I like pure functions. Maybe they have somehow a little cost because they can be complex, but the semantics make the reasoning quite cheap. In OO, every goddamn line is a potential nightmare.

Also, I bet 10$ that until you saw some short yet clean and expressive code, you might believe that the choice is either Java verbosity or Perl cryptics.


I agree with your general sentiment that Java verbosity is actually a strength rather than a weakness. However, the examples given in this particular article are all (IMO) cases where the verbosity truly is unnecessary and is mainly there because for backwards-compatibility. These shortcuts do not reduce readability significantly and I would love to have these in Java.


I think Java is oaky-ish. Much more commonly than core Java itself, Java libraries are being criticized for verbosity. All of those satire AbstractSingletonWorkerFactoryImpls come from real world experience. I personally see a lot of design pattern use for the sake of use without considering benefits that would bring. I see Java code happily introduce two line boilerplate to median call just to shave single line from an edge case. Scattered all over the codebase this makes business logic part of application rather verbose.


You have to create and read more tokens in a verbose language. That takes extra time, and the more tokens, the higher the odds are for bugs to creep in.


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.




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: