Hoping Java will get this one day, but probably not...
Hoping Java will get this one day, but probably not...
Combine those annotations with a linter + pipeline that marks nullability warnings as errors and you've come pretty close to Kotlin's advantages. Of course, Kotlin also has some more advanced mutability controls and other advantages that Java doesn't get for free.
When it comes to simple values, null vs non-null can be solved by using primitives (long) instead of objects (Long), as primitives can never be null.
You can mark the default state. I like to mark everything as NotNull, unless specified otherwise. That way only Nullable annotations are needed at the rare occasion null is a valid value.
And I believe it gives you the exact same guarantees as Kotlin, minus the syntactic sugar — nullability is one of the few things that can be statically analyzed.
Most linters also know the standard library’s nullability information, so it’s quite good.
[0] https://kotlinlang.org/docs/java-interop.html#null-safety-an...
Disagree. The Kotlin way of doing it leads to really subtle bugs in generic code, because T? is usually different from T but sometimes it's not. (For example, if you write a generic cache that caches the result of f(x) in a map, it's really easy to accidentally write code that doesn't cache the result if it's null, and not notice).
Also a lot of the time you don't actually want Optional, you want Either, because you want to know why the value wasn't present. Either is really limited in Kotlin.
1. They are not composable (can't map or flatmap or fold/reduce them).
2. They can only represent one extra value, if you need more, you are back to square one (eg. you can't return an error value, only the fact that there is no value).
If we make another step, one could argue that even optionals are lacking, one should model the possible domain values with sums and products in such a way that no nulls or optionals are required. Do not try this in a language with such a basic type system as Java or even Kotlin though, you will run into the limits of the type system almost immediately.
sealed interface Option<T> permits Some<T>, None<?> {}
record Some<T>(T value) implements Option<T> {}
record None() implements Option<T> {
static <T> None<T> none() { return new None<>(); // can also be a single instance
}
}
The only less than ideal part is that None needs the generic type, but that can be easily circumvented by adding a generic helper method. You can add all the Monad goodies to the Option interface and you will even get exhaustive switch cases with pattern matching. The only thing Java’s type system can’t express is abstracting those Monad goodies, but it can absolutely implement them on a case-by-case basis.The fundamental problem in Java is, however, what you stated in your last sentence: you are limited in abstraction, in most cases you have to implement the specifics.
I never found the talk online, but here's the speaker deck: https://speakerdeck.com/mariofusco/monadic-java
1. They are not composable (can't map or flatmap or fold/reduce them).
Kotlin helpfully added mapNotNull() and similar methods. 2. you can't return an error value, only the fact that there is no value
Yes I much prefer Rust-like return values, the non-local control flow of exceptions leads to convoluted code and improper error handling.Is Kotlin better because it works out of the box or are there differences in the feature set?
But regarding the feature I imagine it is the same. Or are there cases where the Java Null Analysis fails?
Disagree. If you don't PUT the nulls into the language, you don't need a brigade of PhDs to develop the static analysis to tell you whether you have nulls.
I'm sick of worshipping at the altar of backward compatibility. Just because we used to choose to include nulls doesn't mean we need to keep choosing to include them.
Instead of having to wrap an optional value, you just annotate the type as being the union of something _or_ null. You get the same guarantees, but it actually composes openly instead of having to create a closed, specific construct that enumerates variants.
No.