Hacker News new | past | comments | ask | show | jobs | submit login

Lombok supports `@With` on records, which adds wither methods. And there are ongoing JEP discussions on how to support this natively in Java.

Lombok is so ubiquitous today that I effectively consider it part of the language. And even with Lombok, the Java toolchain is so much faster, easier to use, and more stable than Kotlin.

Kotlin is a great language, marginally better than Java, but I'm not sure it's worth enduring the toolchain. And Java keeps improving.




If you think Kotlin is marginally better than Java, you probably have a superficial understanding of Kotlin. Kotlin is vastly better than Java, which isn't surprising because Kotlin was designed 15 years later with the explicit goal of being a better Java and fixing as many of Java's design mistakes as possible. For example, the designers of Kotlin went through the book "Effective Java" and tried to avoid the listed pitfalls by design.

Java will never get close to Kotlin because even though Java can, and thankfully does, still improve, it's too late to revise many of it design decisions, and large language features often need to be considered in a language's design from the start.

On Android, Kotlin is an even bigger win because Android uses an old Java version and has become Kotlin-first.

In my opinion, Java will remain the best choice for libraries that target multiple JVM languages. For everything else, I use Kotlin without thinking twice. Kotlin 2.0 laid the groundwork for toolchain improvements, which have indeed been a long-standing pain point.


Nah: If you think Kotlin is vastly better than Java, then you have a poor grasp of Java. I offer the same evidence you did.

Having worked professionally in both, I find that good programmers can write excellent code in both languages; bad programmers can write terrible code in both languages. The average cultural practice of the Kotlin community is perhaps better than the average cultural practices of the Java community (which huge and defies averaging anyway). But Java code which emphasizes immutability, uses Optional instead of nulls, uses streams, etc is pretty indistinguishable from Kotlin.


I first learned Java in 1997 and have been using it professionally since 2005. If your Kotlin code is pretty indistinguishable from your Java code, you aren't using Kotlin properly. Here is what I wrote on this topic in 2022:

Here are some of my favorite Kotlin improvements over Java that I leverage all the time:

* Much improved type system (nullable types, function types, declaration site variance, type aliases, contracts, better type inference, reified function type arguments)

* Local variables are final by default ("val")

* Type-level distinction between read-only and mutable collections (but compiled to Java collections under the hood, so no conversion required when interacting with Java)

* Much improved collection API

* Much improved lambdas (e.g., no pain points w/ mutating variables and checked exceptions)

* Extension functions (incredibly useful in practice)

* Much better DSL capabilities (great for UIs, generating HTML, etc.)

* Lazy properties (more generally: delegated properties)

* Coroutines (looking forward to Java's Loom; by then coroutines will have dramatically improved my async code for 5+ years)

* Great serialization support (kotlinx.serialization)

* Pragmatic macro-like capabilities via inline functions and compiler plugins (removes lots of boilerplate)

* Multiplatform support (JVM/JS/WASM/native; Graal native image is a good alternative to Kotlin/native and also works for Java)


I'll go through a few of these, but not all:

* Yes, the Kotlin type system is better, no question. And yet it doesn't really matter for 90% of code, Java's type system is "good enough". And in the places where it tends to suffer, IntelliJ ends up covering the ground anyway (validating various annotation values, spring properties, sql, etc).

* The difference between nullable types and Optional<?> types is not material. TBH I quite like Optional<?> and how it meshes with streams.

* "final by default" is indeed nice. But putting `final` everywhere (which we do) is at most a tiny annoyance. This is not material.

* Clear type distinctions between mutable and readonly collections is better, but not material, because we treat all collections as immutable.

* Java streams hit 90% good enough, and StreamEx tends to fill in another 9%.

* We don't have pain points with mutating variables in closures because we don't use mutable variables.

* We don't have problems with checked exceptions because we don't use checked exceptions (and when interfacing with foreign code that throws checked exceptions, we use @SneakyThrows or wrap).

* Coroutines are a major net negative for Kotlin, and Loom is already here. After many years of async programming in JS/TS, Ruby, and (going back far enough) C++, I've concluded that async programming (and function coloring) is absolutely toxic and should be avoided unless it's absolutely required for performance (or the platform, ie GUIs).

* Multiplatform support has great future potential, but it doesn't seem to be ready yet. I just started a greenfield project and _really_ wanted to use KMP. Decided against it in favor of a React frontend. I don't want to live on the bleeding edge, I have work to do. And I've been bitten by deadended technologies enough times in my life to know the risk isn't worth it.

I could go on but I think you get the point - my Java looks like Kotlin, and the parts of Kotlin that would really diverge from my code aren't things I want. You could tell me that I should use coroutines everywhere and I just simply disagree.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

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

Search: