I understand that, but for starting new projects the equation can be different.
> many of us don’t struggle with null
Sorry, but I don't believe there's anybody like that. I guess many people accepted/internalized the struggle.
Anything relying on that is brittle. I work on a 20 years old code base with the majority of the base classes being 15+ years old. This is exactly where Java should shine, and it's not doing bad, but it's very hard to enforce top-notch coding standards across ~200 devs committing during that time frame.
As a result, there isn't much of an information on the nullability of return types, and you end up defensively handling nulls everywhere which is just noise.
> Last time I saw uncaught NPE in server logs was a long time ago
Luckily these are rare also for us, I guess most are caught during development and then on some test environment / CI, but catching these anytime later than compilation is way too late. The cost of a bug is directly connected with time between introduction and fixing it.
I don't think for new projects the equation is any different. Why would I throw away a language and ecosystem that I understand, that I'm productive in and with language designers that I trust to do the correct thing for the future? Why go learn another language and ecosystem when the things it brings to the table are of no value _to me_ ?
I honestly do not struggle with null. If you actually read the code you call and test the code you write you won't have a problem.
The language is conceptually very similar and ecosystem is shared to a large degree.
> I honestly do not struggle with null. If you actually read the code you call and test the code you write you won't have a problem.
Looks like you work on small projects only. If you can just read the code you call, then you don't need much of a type system at all.
In my case, the call I make often executes 10,000s of lines of code and figuring out if it can return null in some cases can take hours or days.
Don’t be dismissive. I could easily just say “looks like you’re just a bad dev if you can’t handle null.”
I work on large code bases too. There’s nothing special about them.
Ideally everything should be non-nullable, with optionally specified nullability.
Of course, in the type system.
Just like we're "documenting" types themselves in the type system and not in JavaDocs.
A notable exception is some kind of generated objects, from say, wsdls getting transformed to different dtos, but in this case MapStruct is a godsend, and is overall less error-prone than doing it even in a null-safe language. (Manually copying fields won’t tell you if a new field is added that should also be copied, for example).
If a function in a null-safe language is declared to return "String", then it signals that, effectively, you don't have to check for null. So, yes, compiler-enforced null safety does reduce the amount of nulls you need to check.
The trick, of course, is to not make everything nullable, but as few things as possible.
In Kotlin, of course, this is only true up to the point where Java interop might interfere with it (because Kotlin might infer a Java return type to be non-nullable when it's in fact nullable).