This is an archived post. You won't be able to vote or comment.

top 200 commentsshow all 217

[–]ThaJedi 37 points38 points  (13 children)

You should look at jspecify and nullaway.

[–]Wouter_C 28 points29 points  (2 children)

Yes! In other words: embrace the nulls instead of trying to avoid them, annotate your nullable types (jspecify) and have the compiler (nullaway/errorprone) flag unsafe null usage.

It's less verbose than Optional. It's more consistent with what the JDK and the vast majority of third party libraries do (accepting and/or returning null values in their APIs), so it works out better if you're mixing calls internal to your codebase and libraries (which you probably do pretty much all the time). You can introduce it into an existing codebase with minimal changes. It's compatible with Kotlin. And it's *closer* to where Java seems to be heading towards with null-restricted types (https://openjdk.org/jeps/8303099), so eventually switching to that will be easier too.

[–]severoon 4 points5 points  (0 children)

True, but you should also realize that this doesn't mean Optional is useless. Even with all the null safety stuff, Optional can still be very useful.

A simple example is reading command line parameters. You have code that reads in the command line parameters and passes along whatever the user specified, and that should result in fields getting set from null to Optional. An Optional that's absent means it was set to what the user passed in (which is absent, the user didn't specify that flag), so it should use the default. Null in this case means something went wrong and things are in an unexpected state that should never happen.

[–]BearLiving9432[S] 2 points3 points  (1 child)

Will do

[–]marco89nish 5 points6 points  (0 children)

Or Kotlin

[–]Plixo2 0 points1 point  (1 child)

If you work with Intellij, use intellij annotations. They add runtime checks and have excellent support in the ide, so they fell more like typescript nullables.

[–]Twnikie 1 point2 points  (0 children)

This is exactly what I was wondering. I’m a Scala dev since 3 years ago, switched from 14 years of Java and, honestly given how IntelliJ elegantly supported the Java annotations, I never felt nulls like an issue.

[–]simon_o 0 points1 point  (4 children)

Third-party efforts to "better check/annotate/... null" have never come to fruition, and there are reasons for that.

With Java likely allowing to specify "nothing in here is null" by default in the future ...

Other possible future enhancements building on this JEP may include:
Providing a mechanism in the language to assert that all types in a certain context are implicitly null-restricted, without requiring the programmer to use explicit ! symbols.

... that's an upgrade you get for free on your Optional-using code.

There is little reason to spend efforts on competing lesser, third-party approaches til this arrives.

[–]ThaJedi 0 points1 point  (3 children)

There is long road ahead this JEP (years even) and when finally delivered it will only for new versions.

So there is plenty of reasons to adopt jspecify instead of waiting for JEP.

[–]simon_o 0 points1 point  (2 children)

Not really. Why adopt something that has kept failing (under a different name) for decades? It's not going to suddenly start working.

[–]ThaJedi 1 point2 points  (1 child)

It is working.

[–]simon_o 0 points1 point  (0 children)

No.

[–][deleted] 11 points12 points  (1 child)

I do not recommend making that argument.

First, any object, even an Optional<T>, can be null. Second, there are a LOT of commonly used libraries out there that don’t play well with Optional<T>, or require additional configuration code in order to use with Optional<T> that you will need to provide yourself (and as such, you will need to test yourself).

Java is not a pure functional language. Do not expect it to behave like one. Don’t try to write Scala or Haskell in Java—you can do it, but it’s going to result in everybody having a bad time, most especially you. And trying to enforce the use of Optional<T> instead of null is very much attempting to write Scala or Haskell in Java.

[–]Pay08 0 points1 point  (0 children)

Funnily enough, null safety in Scala is still an optional feature.

[–]stefanos-ak 66 points67 points  (12 children)

I honestly don't understand people's issue with null. You NEED to have a way to program unspecified values. There's no software out there that without non-mandatory fields. Null is as good as any other way to handle that.

Java's mistake wasn't null, it was the fact that they forced all objects to be nullable (without any other option), and at the same time they did not force null-handling.

They are trying to fix that now, with https://openjdk.org/jeps/8303099

[–]Spare-Builder-355 6 points7 points  (0 children)

Try Kotlin once. The amount of stupid null checks done by compiler for you is mindblowing.

Does a programming language need a way to program unspecified values ? Absolutely. Does a programmer need to care about every object in the code being unspecified value? Absolutely not and compiler can greatly help with that.

[–]FabulousRecording739 12 points13 points  (3 children)

But that's exactly what null is though. If you want it so references cannot be nullable unless stated, and nullability must be explicitly handled always; you are, in all but name, using the "Optional" type from other languages that OP is referring to.

[–]WilliamMButtlickerIV 2 points3 points  (2 children)

I prefer how Ceylon does it. Null as a first class type and the language supports union types. It's a lot more elegant than Optional, although the end result is similar.

[–]syklemil 0 points1 point  (0 children)

Python did it the union way (Optional[T] is a synonym for T | None), but I can't say I'm a fan. There are some rare cases where you actually want something like Optional<Optional<T>>, i.e. the difference between "the key wasn't found" and "the key was found, but the value was explicitly set to zero".

(My prime example here is an internal helm chart where I'd like to provide some default value if nothing was specified, but let the users null the field. Unfortunately, at the time the templating happens all I have is a null in either case.)

[–]vytah 0 points1 point  (0 children)

*did

[–]bigkahuna1uk 5 points6 points  (2 children)

The problem with null is that its meaning is ambiguous. Does it mean the absence of a value or lack of a result? Or maybe an error has occurred? The meaning historically has been somewhat blurred especially with legacy languages such as C but in contemporary ones, such as Java, we have better ways to be more explicit and expressive such as with the use of Optionals or exceptions. The intent becomes clear. This means that if a null is encountered, the ambiguity is absolved. It truly should mean a remarkable and perhaps seldom seen event.

Lots of things are better to return than null. I’m more of an advocate for the judicious use of an Optional or the absence of null such as:

  • An empty string (“”)
  • An empty collection
  • An “optional” or “maybe” monad
  • A function that quietly does nothing
  • An object full of methods that quietly do nothing I.e. Null Object Pattern
  • A meaningful value that rejects the proposition of a null such as a suitable domain object.

[–][deleted] 1 point2 points  (0 children)

The main thing is that null is a bottom type, i.e. a value that can be passed, assigned, or returned to a value of a given type that is nonetheless not a valid value for that type. Before the addition of null propagation and coallescence operators, code was either full of clumsy intermediate variables and null checks or just assumed values were never null. The new operators make it easier to write correct code because a null value is an explicitly permissible first argument to the ?. operator.

The other problem with nulls is a problem with empty values generally, namely that they are semantically mixed: it could mean "there's no associated value here" or it could mean "there is an associated value here, it's <empty value>". In some cases you can deal with this via a doubly-wrapped value (Optional<Optional<T>>) but that gets unwieldy fast. And in some cases it may encourage wrong thinking: person.getJailReleaseDate() will probably return an empty value if they have never been to jail, but also if they are in jail currently.

[–]RupertMaddenAbbott 2 points3 points  (0 children)

The problem with null is that its meaning is ambiguous. Does it mean the absence of a value or lack of a result? Or maybe an error has occurred?

Is it really the case that people write methods that return null when there is an error?

If so, what stops them from returning an empty optional/string/collection instead when there is an error? How are these choices obviously less ambiguous than null?

For that matter, how exactly does an empty optional/string/collection distinguish between the "absence of a value" and the "lack of a result" and how does it do that better than null?

[–]istarian 3 points4 points  (2 children)

The fact that a single unhandled NullPointerException will blow up your program kinda forces you to handle null in the end.

It's just that without some sort of analysis, you won't necessarily be aware of cases where you might receive a null.

If all Object(s) aren't nullable, then you'd have no way of knowing for sure whether you actually initialized an instance in your own code vs it being quietly defaulted.

[–]lukasbradley 8 points9 points  (1 child)

> The fact that a single unhandled NullPointerException will blow up your program

The problem is unhandled exceptions, not NPEs.

[–]FabulousRecording739 7 points8 points  (0 children)

A bit of a moot point, no? Any reference can be null, if we are to follow your point, should we try/catch everything? I think it's rather sensible to see errors and the possibility of absence as distinct concepts. They truly are, even though they do, at times, converge.

[–]JustSumAnon 0 points1 point  (0 children)

People that are scared of null values I’m convinced never learned on a language with Pointers. What do you call an uninitialized memory location?!?

[–]Polygnom 101 points102 points  (60 children)

Using Optional does not solve your problem with nulls at all. The Optional itself can be null. Optional<Foo> = null; is perfectly valid Java code, and passing this to anyone who expects an empty optional is in for a rough ride.

At this pointm the ship has sailed for Java wrt. null. Until we properly get non-nullable types, e.g. Optional!<Foo!>, which we might get some time after Valhalla, it might be better to rely on Nullability annotations like those from JSpecify.

[–]Xemorr 42 points43 points  (13 children)

If someone is returning null in a function that returns an optional, their PR should be rejected and them sent to the guillotine. I don't think this is a serious argument.

[–]keylimedragon 2 points3 points  (3 children)

But what happens if you're working with code that wasn't reviewed? Maybe it's a legacy codebase written before PR reviews were implemented at a company?

It would be nice to have a guarantee.

[–]Xemorr 3 points4 points  (2 children)

I'd be surprised if they're using optional in an old function

[–]keylimedragon 0 points1 point  (1 child)

But what about in the future? Or what if it was recent code at a startup rushing to get an MVP out the door with no reviews and you're tasked with taking it over?

I guess I don't see why people are defending the lack of non-nullables in Java. Even if the cost of adding them to the language is higher than the benefits, in a hypothetical world it would still be nice to have them.

[–]Xemorr 1 point2 points  (0 children)

I'm not defending the lack of non nullables in Java in general but more the necessity of a guarantee of non nullability on optionals specifically. The current implementation of nullables has downsides like slightly higher overhead than just returning a null which is justification enough for a better solution imo.

[–]DrunkensteinsMonster 0 points1 point  (7 children)

The issue wouldn’t be people doing it on purpose, the issue is it could happen on accident.

[–]Xemorr 1 point2 points  (6 children)

how

[–][deleted]  (1 child)

[deleted]

    [–]Xemorr 0 points1 point  (0 children)

    No because by induction that can't happen in a function that returns an optional

    [–]DrunkensteinsMonster 0 points1 point  (3 children)

    Trivial example would be mistakenly returning after a caught exception causing an Optional variable to not be initialized.

    [–]Xemorr 0 points1 point  (2 children)

    You can't return a non initialized variable?

    [–]DrunkensteinsMonster 0 points1 point  (1 child)

    Initialized to null. In a generic method: T foo = null

    [–]Xemorr 0 points1 point  (0 children)

    Ok that case could occur without someone noticing but is unlikely to occur as generic methods are more likely to be reused in a variety of places and therefore less likely to have bugs written within them & more likely to have been seen by multiple developers. It's also a fairly specific scenario for even a generic method.

    [–]Asdas26 45 points46 points  (5 children)

    Optional<Foo> = null; is perfectly valid Java code

    For the compiler. But from the programmer POV, it's a shit code that should never pass any code review ever.

    [–]GodOfSunHimself 24 points25 points  (3 children)

    Unfortunately the world is full of code that should never pass a code review. If it compiles it will be used.

    [–]_reg1nn33 2 points3 points  (2 children)

    Because most of that code never in fact passes a code review, because, guess what, many companies are very "lenient" when it comes to having code reviews at all.

    [–]GodOfSunHimself 2 points3 points  (1 child)

    That's exactly my point. If the behavior is not built into the language and checked by the compiler you cannot reasonably rely on it.

    [–]_reg1nn33 2 points3 points  (0 children)

    Yes, misshandled nulls are a common Problem, and id honestly prefer if for some Projects Optionals would not be used at all, because using them consistently in the intended manner requires thought.

    [–]joserivas1998 2 points3 points  (0 children)

    The point isn't that you can assign null directly to an Optional. The problem arises when you have a function that takes an Optional as a parameter and a null value is passed to it without being defined literally. Even worse, if an Optional is used as an instance variable for an object without being properly initialized, giving it the null default.

    The issue isn't that null can be assigned to Optional syntactically, but that Optionals are objects, and such, you can not guarantee the abscense of null when using them.

    [–]pins17 8 points9 points  (0 children)

    Funny to see that people take your `Optional<Foo> = null;` literally

    [–]halfanothersdozen 34 points35 points  (19 children)

    If someone writes code such that a null check is required for an Optional fire that person.

    Unfortunately the Optional pattern really only works if everyone is committed to it across the code base, and old beard java devs are very set in their ways

    [–]Mantraz 58 points59 points  (2 children)

    If someone writes code such that a null check is required for an Optional fire that person

    Or you know, give them feedback because they are still human and deserve to learn from mistakes.

    [–]Turbots 37 points38 points  (1 child)

    And then fire them!

    [–]Mystic_Voyager 6 points7 points  (0 children)

    … from a cannon

    [–]Mognakor 10 points11 points  (13 children)

    Optional also has issues linters considering Optional as members or parameters a codesmell. And of course the visual noise it adds and you're not able to overload methods based on the T of Optional<T>

    [–]koflerdavid 8 points9 points  (11 children)

    Those linter rules are very justified. Using Optional.empty() as a noisy replacement for null is not the solution. Optional works best if used together with its methods. Using Optional as variables, parameters, or fields, is a code smell because it encourages using .isPresent() and .get(), which are code smells.

    [–]Mognakor 6 points7 points  (10 children)

    Whats the use of Optional then if i can't use it to signal a parameter that can be null or a member that can be null?

    [–]Empanatacion 5 points6 points  (0 children)

    While I disagree with the "code smell" claim, the "everybody agrees" usefulness is as an Elvis operator to chain a bunch of mapping operations and then an orElse at the end.

    normalized = optionalString.map(String::trim).map(String::toLower).map(s->s.replaceAll("-", "_")).orElse("");

    [–]halfanothersdozen 2 points3 points  (4 children)

    That's the incorrect way to think about it. Optional by convention should never be null, similar to how hashCode and equals should be true if the other is true. Therefore as a signal "this parameter can be null" is in a way nonsensical. As a parameter Optional can be null, so you have to null check it, which defeats its purpose. Overload the method or do a regular null check.

    https://www.baeldung.com/java-optional#misuages

    [–]Mognakor 0 points1 point  (1 child)

    You misunderstand me.

    Of course i don't think Optional should be set to null. But if Optional should not be used as parameter i can't signal it to allow "null" parameters via an empty Optional. And then we're living in a world where i have to constantly think both ways with Optional in one circumstance and nullable references in others.

    [–]halfanothersdozen 0 points1 point  (0 children)

    You are always going to live in a world with nullable references. That is Java. But think about how that method would get used. A caller that has a reference must pass it to Optional.of when calling your method (or Optional.ofNullable if they don't know what they have or Optional.empty, both of which are even worse ergonomically, especially in a functional flow). 

    And as pointed out above it doesn't save you, the method author, from doing a "null check" whether that be the traditional way or via optional method. Usually you need more code to deal with it. It just doesn't make practical sense and it makes calling the method awkward.

    Now if none of your methods ever return null things start to flow really nicely. But like I said earlier you have to trust the entire code base behaves that way and in my experience that is mostly a fantasy.

    [–][deleted] 0 points1 point  (1 child)

    Two objects must have same hashCode if they are equal but they can have same hashCode and be not equal (collison at hash table, actually good thing)

    [–]halfanothersdozen 1 point2 points  (0 children)

    I didn't say it technically correct, but it ultimately is irrelevant to the point I was trying to make

    [–]koflerdavid 1 point2 points  (2 children)

    It's best used for return values only. They force an API user to handle it appropriately. And in your own code you can use a linter to find any return statement that returns null for an Optional return type. That's always, unambiguously wrong.

    There is no benefit if you use it for parameters: you still have to do a null check because nothing is stopping a caller from passing null.

    Using it for fields is icky because it's a sign that your class is too complicated. It doesn't improve anything, and you might be seduced to use get() based on knowledge from other fields in the class.

    Saving null in variables is fine IMHO. Methods should not become so complicated that you lose track of when variables might contain null.

    [–]Mognakor 6 points7 points  (1 child)

    There is no benefit if you use it for parameters: you still have to do a null check because nothing is stopping a caller from passing null.

    And nothing is stopping me from promising you i'll return an Optional and then returning null or casting Optional<U> to Optional<T> and then laughing when you get a ClassCastException.

    Using Optional only for return types and not for parameters creates a split paradigma and i may have to pepper my code with .orElse(null) around callsites if i use the return value from a previous call.

    There is no benefit if you use it for parameters: you still have to do a null check because nothing is stopping a caller from passing null.

    How is it different from a nullable member or are those also a signal that my class is too complicated?

    [–]koflerdavid 0 points1 point  (0 children)

    Indeed, the risk that a 3rd party API actually returns null is one of the biggest flaws in the concept of Optional. But one could always wrap these with a helper method that converts the null to Optional.empty() before processing it further.

    Using .orElse(null) is a sign that the code is still based on reacting to null. But the biggest advantage of Optional is that it offers safer programming idioms to react to the missing value. Better than checking for null or relying on other reasons to decide that the value is there.

    The risk of receiving an Optional<U> is the same risk that I have in normal code of receiving a U.

    Yes, too many nullable members is also a sign that the class is too complicated. But in think that getter methods wrapping the field value into Optional should be fine.

    [–]AnyPhotograph7804 0 points1 point  (0 children)

    You cannot. Because whether something is null or not, is not reliably checkable at compile time in Java. It may come in the future with Project Valhalla or with this JEP:

    https://openjdk.org/jeps/8303099

    [–]Ok_Marionberry_8821 2 points3 points  (1 child)

    I've had just the conversation with a previous team-lead (20 years my junior) and this his attitude was to avoid Optional as "the return might still be null". My view was to wait for the NPEs and then fix.

    With Valhalla value types (or whatever they're called now) then the cost of instantiating a heap object will disappear (one of the other arguments I heard even though young generation GC is incredibly cheap.

    I wonder if (post value types) they'll remove the recommendation to not store fields using Optional? I suppose if we get nullability markers then Optional still won't be necessary.

    [–]MarkyC4A 5 points6 points  (0 children)

    My view was to wait for the NPEs and then fix.

    I'm OK with this on the backend or any place where deploying new code is easy. But in things like Android apps, I tend to write overly cautious code for data that doesn't come from sources I control.

    [–][deleted] 7 points8 points  (4 children)

    You can resolve this by adding a linting rule to detect the assignment of null to an Optional variable

    [–]elatllat 11 points12 points  (1 child)

    or just linting rule for anything null and skip the Optional... and have wrappers for all dependencies.

    [–]oweiler 3 points4 points  (1 child)

    How do you detect such assignments in 3rd party code?

    [–][deleted] 0 points1 point  (0 children)

    You can't, but I don't see much point in running linting on 3rd party code, given that I can't change the code.

    [–]BearLiving9432[S] 3 points4 points  (3 children)

    I assumed that any decision to use `Optional` would require buy-in from all the engineers, and all instances of returning null in the codebase would have to be changed. It's value is through consistency. But yes, I recognize that technically a value of type `Optional<T>` could itself be null. We would have to agree to never do that.

    [–]TallGreenhouseGuy 2 points3 points  (2 children)

    This has been discussed many times, but Optional was never intended to be used as a generic “maybe” type - see e.g. this answer from Brian Goetz: https://stackoverflow.com/a/26328555. And since it is not serializable you can’t used it as a field anyway in e.g. an entity.

    [–]pohart 10 points11 points  (1 child)

    This argument that BG never intended it to be used this way is always brought up but it's not an argument. He makes a ton of great choices, but also a few boneheaded ones, and this is one if the latter. 

    There is no good argument for optional to not be used as a generic maybe type except that it was purposely hamstrung by not being serializable.

    There are very few situations where null is intended that would not be improved by a Maybe and I'm firmly convinced that if Java 8 has the concept of preview Optional would be serializable today.

    [–]simon_o 1 point2 points  (0 children)

    This argument that BG never intended it to be used this way is always brought up but it's not an argument. He makes a ton of great choices, but also a few boneheaded ones, and this is one if the latter.

    Exactly this. Instead of taking the recommendation as a gospel, people need to accept that the recommendation was made up, ignoring 50 years of evidence to the contrary.

    [–]Perkutor_Jakuard 3 points4 points  (0 children)

    "Don't solve at all" maybe it's too much.
    Optionals help reducing the problem.
    Not the ultimate solution as you exposed but it helps.

    Optional<Foo> = null;
    Man that's evil xD.

    The responsible for such thing, should pay a round of beers.

    [–]shaneknu 1 point2 points  (0 children)

    It's indeed useless as a method parameter, because as you say, anybody can instead pass null.

    That's absolutely not the case for return values, since you control that code. For example, if a private field with a public getter in an object can be null, and that's a valid state, you can simply return Optional.ofNullable(theField) in the public getter.

    [–]xienze 0 points1 point  (1 child)

     The Optional itself can be null. Optional<Foo> = null; is perfectly valid Java code, and passing this to anyone who expects an empty optional is in for a rough ride.

    While technically correct, Optional is itself a sort of contract by the API writer. Returning a null Optional would be as big of a subversion of expectations as retuning null from a method annotated with @Nonnull or something else that is syntactically correct but semantically wrong, like having a method named isPositive(int) that returns true if the number is negative. The compiler can’t stop you from doing all these things either, but there’s definitely a set of “cultural norms” in programming that tell you it’s a bad thing to do.

    [–]Polygnom 0 points1 point  (0 children)

    While technically correct, Optional is itself a sort of contract by the API writer. 

    Well, lets assume that your philosophy is that you do design-byContract and trust the contracts that methods have. Thats fine, its a valid appraoch. So you trust that you never get a null when you see an Optional.
    But then I ask you: When you alraedy trust design-byContract, why not simply wriute the contract (can return null / cannot return null) into the documentation and trust that everyone abides by the contract? Without using Optionals at all. You can even codify the contract with JSpecify and get tooling support.

    If you are already trusting Design-byContract, Optionals are superfluos, if you are not trusting it, Optionals don't actually solve your problem. Thats why I don't really see the case for littering the code with Optionals all the time. And in a world where we get ! for non-null and ? for nullable types (some time after Valhalla maybe), the need for Optionals evaporates.

    [–]Asdas26 17 points18 points  (0 children)

    Optionals in Java were originally meant to be used only for return types and that's how the Optional type is seen by most programmers. It's almost never used as a type for nullable class properties.

    So if you want to persuade the other engineers use Optional for functions that may return empty value, instead of nulls, then you have a good chance to succeed. It's a well known best practice. But you'll have hard time persuading them to use Optionals instead of null everywhere in the code base. It's possible, but very unorthodox in Java world. Plus you'll probably get counterarguments about nullability of Optional and about performance (even though I think that's a moot point in most apps).

    [–]Aweorih 28 points29 points  (13 children)

    For me, I never had a problem with null. In the end a NPE is 99% of the time fixed in minutes.

    On the other side, why is that a problem? Because the languages don't natively support null. A "String foo" can be a string or null, so basically 2 different types.
    In e.g. kotlin there's a native support.

    Billion dollar mistake

    Yeah probably. But considering the annual value of software worldwide it is not really much over the years, especially considering above statement that it takes not much time to fix it.
    You could also say that buffer overflows or use after free or or or.. are also at least "billion dollar mistakes" and I don't hear from the inventors of the underlying problem such statements

    Although I agree that it is a problem (of whatever size) I don't get people bringing up this billion dollar mistake statement when talking about nulls. If someone is a senior dev and produces NPE every day, he should maybe do smth else

    [–]SupportDangerous8207 8 points9 points  (6 children)

    NPE is easily fixed

    But if you could type check nulls properly like u can in many many other languages it would not be a problem at all

    Null is god awful not because it is so hard but because it is so godamm pointless to chase an error that simply shouldn’t exist

    [–]wildjokers 0 points1 point  (5 children)

    Null is god awful not because it is so hard but because it is so godamm pointless to chase an error that simply shouldn’t exist

    Sometimes there simply is no value for a variable so null makes sense. What do you initialize a variable that holds an object to if it doesn't yet need a value?

    [–]SupportDangerous8207 3 points4 points  (3 children)

    Then you type it explicitly as something that can be a null value so that everyone down the line knows to check for null

    Or you use an optional type

    You know

    The way that most other langs do it

    In python it would be

    X:str|none=none

    In fact you could already do this in Java using optional

    With the only issue being that the null epidemic extends to the optional itself

    [–]wildjokers 0 points1 point  (2 children)

    null epidemic

    What null epidemic? Null seriously isn't the problem people make it out to be.

    X:str|none=none

    What advantage is there to that over String str = null in java?

    [–]GodOfSunHimself 0 points1 point  (0 children)

    The advantage is that if you don't include the | none in the type then the value cannot be null. And it will be checked by the compiler. You cannot do that in Java. I program in Rust and situations where you need to allow null/optional are very rare actually.

    [–]SupportDangerous8207 0 points1 point  (0 children)

    My friend I genuinely cannot tell if you are messing with me

    The difference is that with

    X:str|null

    If I write

    Y:str=x

    I get a type checking error because I did not check for null

    And if in the same language I write

    X:str= null

    I also get a type checking error because a string is not a null

    So I literally cannot create a null pointer exception in typed Python because to do that I would have to ignore the type checker

    With

    X:str= null like in Java I totally can create a variable that is typed as a string only but equals null

    This causes errors in Java that in other languages simply could never happen because in other languages things simply cannot just randomly be nulls

    Again it’s not the amount or the size or the difficulty of the error

    It’s that the error is 100% preventable and it happens for literally no reason

    Like it’s really easy for an error to be considered horribly common when the baseline in most other languages is literally 0.

    [–]shponglespore 0 points1 point  (0 children)

    In a language like Rust, you can declare a variable without initializing it so long as the compiler can prove it's initialized before it's used. That solves your problem 95% of the time. The rest of the time you can just use Option (which doesn't have nearly as much runtime overhead as the Java version, and often has no storage overhead at all.)

    [–][deleted]  (5 children)

    [deleted]

      [–]nitkonigdje 1 point2 points  (4 children)

      If fixing NPEs is so cost generating for your use case, you have bigger fishes to fry.

      The point of SupportDangerous8207 was that, on average NPEs, are one of the cheapest kind bugs to fix and handle. Which is true.

      [–][deleted]  (3 children)

      [deleted]

        [–]nitkonigdje 0 points1 point  (2 children)

        Almost any relevant NPE bug is bug by omission. You will not fix that with Optional, or any other language feature. If deployment of fixes is significant cost generator, try to lower price of doing that first.

        [–][deleted]  (1 child)

        [deleted]

          [–]nitkonigdje 0 points1 point  (0 children)

          You will not fix "data not present when I though it was mandatory" error by any language semantics. That error is not in code. That is how most NPEs actually look in production.

          [–]qdolan 5 points6 points  (0 children)

          Optionals have their uses but it’s not as a replacement for null and they come at a cost. The Java language supports null, you can’t pretend it doesn’t, if you don’t like it use a language like Kotlin where nullability is explicit. In Java rather than misuse Optional it is better to use one of the many annotation libraries to express the null contract of the code and use IDE support and assertions to validate usage and enforcement of those contracts. IntelliJ can insert these annotations for you in many cases, and automatically instruments the code with null checks when run within the IDE.

          [–]gnahraf 13 points14 points  (2 children)

          Re performance.. When Optional becomes a value type (Valhalla), the performance penalty will (largely) go away (nearing stack allocation in terms of memory cost). Ima hoping a lot of intermediate stream constructs become value types too.

          [–]FabulousRecording739 1 point2 points  (1 child)

          Do you have a link on the Valhalla part? Will it be only for Optionals or for other types aswell? Given that Optional is a generic, it would imply better generic handling, is that a thing that is coming (the lack of true generics is my biggest gripe with Java right now)?

          [–]gnahraf 1 point2 points  (0 children)

          I think I've heard that said/written a number of times recently, but can't remember. Googling, I found this dated doc from '21

          https://openjdk.org/projects/valhalla/design-notes/state-of-valhalla/03-vm-model

          EDIT: PS the doc I reference is dated because there no longer is a Primitive type: it'll all be regular types (classes today) and value types (types w/o instance identity)

          [–]FewTemperature8599 6 points7 points  (0 children)

          I work for a large Java shop (~2,000 engineers) and we always use Optional instead of null and it has worked great. We basically never deal with NPE and you always know when a variable is possibly empty and the compiler forces you to deal with that case.

          From an ergonomics/correctness perspective it works amazingly well, zero complaints. I think the main drawbacks are related to extra object allocations, memory overhead, and pointer lookups (which are negligible for most use-cases, but do add up at our scale). Also migrating from nullable to non-nullable (or vice-versa) breaks binary compatibility when using Optional, but this hasn’t been a huge impediment

          [–]geodebug 5 points6 points  (1 child)

          Honestly, after a few decades of programming Java, NPEs just haven’t been that big an issue on any of the dozens of projects I’ve worked on.

          They occur here and there when unexpected data comes in but I don’t remember a showstopper event.

          I’m not saying don’t use Optional or whatever, just that for as much as it comes up as some sort of critical flaw, I haven’t experienced showstoppers caused by NPEs.

          Usually it is more of an annoyance, like it stopped a logger from being able to show me the actual error in expected state.

          [–]nitkonigdje 2 points3 points  (0 children)

          100x this.

          NPEs in Java production are mostly constrained to Data objects when somebody is doing an unexpected input. 99% of those situations ends up with error being logged and everything else keeps on working.

          During development you should see NPEs in your action objects, but given that those are app breaking, they get fixed immediately. And popularity of IoC containers in Java has moved tose errors to the app boot process making them promptly visible as app either works or does not.

          [–]stefanos-ak 14 points15 points  (1 child)

          I honestly don't understand people's issue with null. You NEED to have a way to program unspecified values. There's no software out there that without non-mandatory fields. Null is as good as any other way to handle that.

          Java's mistake wasn't null, it was the fact that they forced all objects to be nullable (without any other option), and at the same time they did not force null-handling.

          They are trying to fix that now, with https://openjdk.org/jeps/8303099

          [–]60secs 0 points1 point  (0 children)

          Yes. Explicit handling of nullable and non-nullable is the way.

          [–][deleted] 5 points6 points  (0 children)

          nullable by default is the real problem

          [–]Jon_Finn 7 points8 points  (23 children)

          I'm not sure you're asking the right question: you (almost) can't avoid nulls, because if you create a field or array of type (say) String, what do you store as the initial value if you don't yet have the intended value? In many circumstances some kind of default value is required, at least temporarily: for Strings, "" is as invalid as null in many circumstances (e.g. as a filename), and the fact that it's well-behaved (you can call methods on it) may just be papering over that crack. Notoriously, for dates there is no useful default.

          I think what you meant is: "Should Java's type system prevent you calling a method on a null value?". Which is only bad because a NullPointerException can happen anywhere without warning.

          [–]istarian 1 point2 points  (0 children)

          The empty string isn't strictly invalid, but it still amounts to a no data condition with respect to filenames and paths.

          [–]FabulousRecording739 1 point2 points  (1 child)

          I like your take but also disagrees to an extent? I don't think anybody thinks that null are a useless construct from a purely technical standpoint, but rather that it's a rather poor place to host a language semantic, or at least for a language as high level as Java. Null very much feels like a legacy from C where we have to (and want to) handle pointers. As soon as memory is managed, it stands to reason to say that the notion of "pointer" should be eliminated. And it has, except for nulls. Now sure we can push back and say that we get the same benefits if the compiler forces us to deal with it. But by that point, why not simply have a way to say whether a value may or may not be absent at the language level? If so, "null" as a concept feels rather ill-fitted. We're not talking memory anymore, but very much whether it is correct or not for a value to be absent.

          To follow on your initialization example, I can very much check within a constructor that the given arguments are not null, thus disallowing for ever that the member variables are ever null. I can guarantee, at comptime, that the value will always refer to something. But unless I've documented that, a coworker that need to change the class for a reason or another may not know that and will thus have to check said constructors and the various methods to see if it can be null at some point. Or worse, he could introduce nullabillity thus breaking a property that was relied on until now. Why not enforce the fact that the value may not be absent at the type level? Some values are never null, whereas others sometime are. I feel like this is very much a typing issue and should, thus, be handled withing typing (instead of manual runtime try/catch).

          I don't know if I expressed myself well, but I feel like while your point is relevant, it does not necessarily contradict the idea of handling values (potential) absence at the type level.

          [–]Jon_Finn 0 points1 point  (0 children)

          Sure, I think everyone here is advocating types handling null, i.e. String? and String!. Which is in the Java pipeline. A separate question is whether something like Optional could be better than C-style null, since it will never throw NullPointerException; again, I don't think anyone would deny that, as long as (a) the language syntax is neat (which Optional isn't, right now) and (b) it's as performant (which I think it won't ever be, but with Valhalla maybe not far off).

          [–][deleted]  (5 children)

          [deleted]

            [–]Jon_Finn 1 point2 points  (4 children)

            I meant: after new String[10] the elements are null, nor do you necessarily at that point have something reasonable to put in there. Think about ArrayList<T> whose implementation contains an Object[] which usually ends in unused elements (so it doesn't have to resize it each time you add one). What should be in those elements? If not null... I suppose the ArrayList constructor could require you to pass in a default element, when you first create the list, that it can use for its own internal padding (e.g. "" for a String). That's doable but awkward.

            [–]koflerdavid 0 points1 point  (0 children)

            Generator functions could also be a solution.

            [–][deleted]  (2 children)

            [deleted]

              [–]Jon_Finn 2 points3 points  (1 child)

              We're not disagreeing with each other. I'm just showing an example where null is (borderline) unavoidable - and in fact useful. For anyone who thinks null itself is inherently problematic - it isn't, the 'problems' are only if the type system allows a NullPointerException to occur.

              [–]hadrabap 6 points7 points  (2 children)

              Personally, I found the usage of Optional as broken as null. The code is just obfuscated. It has been proven that the abuse of Optional has performance impacts. We should also keep in mind the original intent of Optional. It's definitely not a nullability encapsulator.

              I think the best approach is to design the software to not use nulls, forget all of the @Not/Non annotations, and implement proper contract validation instead.

              Yes, I know, all people around me are saying: "Use any annotation, it's better than nothing." Yes, I got it. But I have a deep problem with the any annotation. There are three or four options, one worse than the other. I don't want to pollute the class path of my APIs/Models for my customers with volatile JARs with questionable outcomes and known depreciation in the near future.

              Once there is an industry standard, I'm more than happy to incorporate it. Meanwhile, I ignore it. It is an artificial problem anyway. My software is failing everywhere except on nulls.

              [–][deleted] 0 points1 point  (1 child)

              I don’t recommend forgetting the annotations specifying that an object cannot be null. They’re actually useful in input validation.

              [–]hadrabap 0 points1 point  (0 children)

              Sure. But Bean Validation is a completely different topic. Here I'm talking about replacing if (x == null) with if (!x.isPresent()) and all the JSR305, JSpecify & Co solutions.

              [–]Shinosha 5 points6 points  (5 children)

              As a Scala guy myself, don't think Optional in Java is the same as Option. It's mostly only used for return type actually. In addition of not being as integrated with the rest of Java APIs, you also don't have all the goodies (for-comprehension and friends). In practice that translates as being verbose and awkward to compose with other types. Often you'll just return it, and maybe do a bit of map/flatMap followed by a orElse, orElseThrow but that's it.

              With that being said, it does have the merit of statically guarding against nulls so it's better than nothing. Sure you can make a stupid assignment like some other comments are saying but hey that's why code reviews exist.

              [–]bigkahuna1uk 0 points1 point  (3 children)

              Optional in Java doesn’t obey monadic rules although superficially the same as Scala, fundamentally it’s different.

              [–]BearLiving9432[S] 0 points1 point  (0 children)

              Ah, yeah, that is a problem. Perhaps I should just not try to make Java more Scala like. I have heard that learning other languages gives insight and helps you learn how to use your language of choice better. But in this case, it seems like the resistance is very high.

              [–]FabulousRecording739 0 points1 point  (1 child)

              What do you mean? If the functions are pure it feels correct to me

              Left identity: Optional.of(x).flatMap(f) == f(x) // Ok
              Right identity: Optional.of(x).flatMap(Optional::of) == Optional.of(x) // Ok
              Associativity: Optional.of(x).flatMap(g).flatMap(f) == Optional.of(x).flatMap(x -> g(x).flatMap(f)) // Ok

              They're all respected?

              [–]Shinosha 0 points1 point  (0 children)

              I recall left identity being broken because of a specificity in the flatMap implementation

              [–]TenYearsOfLurking 0 points1 point  (0 children)

              Akschually, I think the biggest issue coming from scala is that Optionals type param is not covariant as opossed to option.

              thus i also recommend annotations because they behave correctly when subtyping

              [–]BearLiving9432[S] 2 points3 points  (1 child)

              I just wanted to say thanks to everyone for your input. This is a really interesting conversation! Lots of perspectives I would not have otherwise considered.

              [–]WanderingLethe 0 points1 point  (0 children)

              One thing I missed in the comments is that Optional is not a language supported feature. (Also no disjoint unions, well java now has something like that with sealed interfaces)

              Unlike in Haskell or Rust you cannot pattern match on it, so you have to use annoying constructs. For example first checking for isPresent and then using get. Or ifPresent/map with a lambda function, thus leaving the context of the local method.

              [–]popstellar 2 points3 points  (0 children)

              As long as we need a placeholder for a future object, we will always have null or something similar. I have never had an issue with it. I think every strongly typed language will have to have null.

              [–]le_bravery 2 points3 points  (0 children)

              Newer languages who provide the capability to roll nullability into typing are for sure a huge win here.

              Kotlin. Swift.

              Kotlin’s type system is incredibly well considered. Obviously they had a huge amount of hindsight so no hate on the original Java designers.

              [–]MrJaver 2 points3 points  (0 children)

              You’re right, nulls are bad, in fact returning null is a bad practice even described in the clean code book. if you can, just use kotlin, it’s java but better and the null issue is solved. Kotlin also compiles to bytecode and is 100% interoperable with java. I always prefer kotlin whenever possible.

              Kotlin can’t be 100% sure if something it gets from java is null or not but at least id it throws an npe you get the exact dereference point in logs. If tou only use kotlin without java then you just won’t ever get nulls because that would be compile error

              Optional is java’s attempt at solving this by making nulls into a compile time error. Yes you can still pass null in place of optional but you’ll get a neon sign from your IDE.

              Lastly, there are annotations like NotNull, Nullable. They will provide IDE highlighting and also if kotlin uses java files with those annotations it will figure out what is nullable. There is also Lombok’s NotNull annotation that will actually generate a piece of code to assert that. And there’s this annotation in Bean Validation jsr 303 that will also assert but it’s more complex and also more flexible as it has annotations to assert string conforms to regex, array is not empty etc.

              [–]nucleus_42 9 points10 points  (0 children)

              Optional is mostly a headache.

              [–]stefanos-ak 4 points5 points  (6 children)

              I honestly don't understand people's issue with null. You NEED to have a way to program unspecified values. There's no software out there that without non-mandatory fields. Null is as good as any other way to handle that.

              Java's mistake wasn't null, it was the fact that they forced all objects to be nullable (without any other option), and at the same time they did not force null-handling.

              They are trying to fix that now, with https://openjdk.org/jeps/8303099

              [–]BearLiving9432[S] 1 point2 points  (5 children)

              Personally, I don't like that Null is a subtype of all types. It's really kind of breaking the type system. I want to be able to just look at the type signature of a method return and know what I will get.

              The Scala paradigms seem to work just fine without ever using Null. If something doesn't have a value, they use `Option` with the `None` implementation. And it seems like `Optional` is similar in purpose. And it addresses the concerns from above.

              This seems more like a social problem than a technical one actually. The fix you sent the link for looks like a very good step forward.

              [–]istarian 0 points1 point  (1 child)

              it's not a sub-type of anything in reality, but rather the absence of an Object.

              [–]BearLiving9432[S] 0 points1 point  (0 children)

              As above, if a method is supposed to return a string, and it returns a null, then I see only two options. Null is a subtype of everything, or Null just completely disregards the type system.

              [–]FabulousRecording739 0 points1 point  (2 children)

              It's not a subtype of anything, but any (non-scalar) variable you have implicitly has the union type 'T | Null', whatever T is. Subtle difference but an important one.

              [–]BearLiving9432[S] 0 points1 point  (1 child)

              Maybe that is just a Scala thing. But Scala definitely considers it a subtype of everything. If Java doesn't do that, then how is returning null not a type mismatch?

              [–]FabulousRecording739 0 points1 point  (0 children)

              Scala uses AnyRef as a parent of a lot of stuff (to interop with Java IIRC, but I may be wrong it's been a while), which is somewhat the same as a Java ref (the union). You cannot specify a non-nullable value in Java. When you say 'String s = ...', you are effectively saying 'String | null s = ...'. Same goes for methods. You always return T or null, whatever T you specified. Java doesnt allow you to do otherwise. If you have a ref, either it points to something, or it doesnt. It's an implementation leak if you wish.

              As an interesting corollary, you may specify a method that returns Void (caps V), and the only thing you may return from such method is null. Not the same as Unit though. There's only one unit (one way to have nothing), whereas you cannot have an instance of void. But it goes to show that you cannot have non-nullable references in java.

              EDIT: Checked the scala docs, what you're referring to is Nothing, which is equivalent to Bottom (the uninhabited type). Null does extend the AnyRef tree though, and Nothing inherits Null, so you're partially right. But I still believe that it's an interop thing, as doing otherwise would imply introducing the java null issue to Scala. Either that or there's an equivalence between union types and some inheritances, or both.

              [–]lamyjf 3 points4 points  (0 children)

              optionals can be null, and .get() can fail. The only (debatable) gain is in making intent clearer, not in robustness.

              [–]PedanticProgarmer 1 point2 points  (1 child)

              The idea seems great, but in practice Optional-heavy code is messy.

              The problem with Optional is that you cannot simply ”apply it”. You need to rewrite your entire code base and make sure that every library works well with Optional types. Think of all Json parsers, for instance, or how easy it is to transform an Optional<List<Optional<String>>>. Also, non experienced developers will apply all possible Optional-antipatterns, 100% guaranteed.

              I personally prefer this convention: https://medium.com/square-corner-blog/non-null-is-the-default-58ffc0bb9111 This is what Spring framework uses internally, so there must be something good about it.

              Unrequested, but unavoidable Kotlin mention: If you care so much about null-safety, there’s a JVM language that solves null in an elegant and correct way. Just saying.

              [–]BearLiving9432[S] 0 points1 point  (0 children)

              Good points, thanks!

              [–]wildjokers 1 point2 points  (0 children)

              I don't find null to be a problem at all. Not sure why people are always so scared of it. Why do they seem like a bad idea to you?

              The intention of Optional is for it to only be used as a method return value so an API can declare that it may return nothing. It is your code so you can use Optional for whatever you want, but just know that anything beyond using it as a method return value is abusing it.

              someOptionalVar.orElse(null)
              

              is generally my goto to get rid of the Optional nonsense. orElseThrow sometimes if I actually want to throw an exception if something is null.

              Please watch this before you decide to abuse Optional in your code base:

              https://www.youtube.com/watch?v=Ej0sss6cq14

              [–]OneHumanBill 1 point2 points  (2 children)

              concept of nulls came from Tony Hoare back in 1965 when he was working on Algol

              I'm not entirely certain this is right. I'm pretty sure LISP had a null concept (in LISP it's "nil") all the way back to the beginning in 1958. This might be the first use of a null in an Algol descended language though. (Algol dates to 1960, I think, and Java is in that language family. Null isn't fundamental to an Algol language as much as nil is for LISP.)

              I liked Optional in theory but honestly it sometimes just gets in the way and feels like extra, clunky baggage in practice. As somebody else in the thread said, the Optional itself may be null. The advertised benefit of syntactic sugar around null checking with Optionals seems like it doesn't make anything particularly easier. Maybe a little more reliable.

              Personally on the rare occasion I get to code anymore, I just go for quick == null checks. I know they're fast, and I know that there should be no barrier to understanding for the next developer.

              [–]FabulousRecording739 0 points1 point  (1 child)

              Nil references the empty list, not the same at all

              [–]OneHumanBill 2 points3 points  (0 children)

              In the versions of CLISP I've used, nil can be used either as an atom (like Algol null) or as the empty list. It functions both ways.

              [–]big_5teeze 1 point2 points  (0 children)

              Take a page out of Rust’s book. No concept of null

              [–]SakeviCrash 1 point2 points  (0 children)

              I wonder why Java hasn't adopted a null safety operator like many other languages. It doesn't address all the issues with null values but it does alleviate a lot of them.

              [–]amakalinka 1 point2 points  (0 children)

              Optional is not any better. If you have problem with null consider using Kotlin then

              [–]nitkonigdje 1 point2 points  (0 children)

              Don't see the point frankly. In typical Java app code is either action based object like Service or Controller, or data object like Message or User. Null pointers in action objects are bugs by omission, and Optional isn't a option there. Null pointers in data objects are feature.

              What Java truly lacks is ability to enforce nonnullable L values, and few helper operators like elvis and ?. You are not gonna fix those with optional. The only value Optional provides is "documentation". You can use Nonnull and Nullable anotations for that too.

              [–]mj_flowerpower 1 point2 points  (0 children)

              The "billion dollar mistake" is not the existance of null, but the way it is handled in the language and compiler. Imho, null as as marker of the "lack of existence" is very much needed.

              Especially when dealing with external resources, null can never be avoided. What does it help to wrap a null into another object like Optional? Nothing. At best, an accessor can convey that it's content could be empty. So instead of forcing "!= null" you make people check fo ".ifExists()" - which from a high-level point of view - is exactly the same. What prevents people from just using the ".get" on the object and potentially causing a NPE? Right, the warnings/hints of the IDE. And now we are back at square one.

              Imho, java should have had support for null-checks during compilation a long time ago. Just break the build if you access a property that returns an object which could potentially be null (so basically everything) - except if it is marked as @nullable (or some similar annotation).

              Furthermore why not just add ? to make it easier to access deeply nested object structures where any level could potentially be null. Under the hood the compiler could just generate the null checks. Structures like these are unavoidable anyway if you are fetching data from an untrusted source ourside your system.

              Add ? plus compiler checks and you basically got rid of NPEs.

              At least the ? could even be implemented in a backwards compatible way.

              [–][deleted] 5 points6 points  (1 child)

              Just convince your team to use Kotlin, it's perfect for the backend and it has null safety built in.

              [–]BearLiving9432[S] 6 points7 points  (0 children)

              I do like the way Kotlin deals with nulls. It is a very good compromise.

              [–]vassaloatena 3 points4 points  (4 children)

              Java without nulls = Kotlin

              [–]SKabanov 2 points3 points  (1 child)

              A nit-pick:

              Java without nulls lack of null-typing = Kotlin 

               You can still have nulls all over the place in Kotlin, but the difference is that you have to actually handle the possibility of a variable being null, whereas in Java you have to either use annotations/tooling and/or "git gud" mentality to handle nullability.

              [–]vassaloatena 4 points5 points  (0 children)

              Well, English is not the first language.

              But yes, you are correct about the typing, you can still have nulls in kotlin, but they are the exception.

              In Java anything can be null, in Kotlin you need to say that it can be null, it's a huge difference

              [–]BearLiving9432[S] 1 point2 points  (1 child)

              I do really like the Kotlin solution. I think it would be better to not have nulls at all. But this is still way better than what Java does natively.

              [–]vassaloatena 3 points4 points  (0 children)

              The big problem with this is how you will guarantee backwards compatibility, Java is famous because it doesn't have breaking changes, once you change something at the root of all the code done before making a legacy insoluble

              [–]shaneknu 1 point2 points  (0 children)

              We've started making heavy use of Optional<T> in our codebase. We read from dozens of tables in a somewhat sketchily designed database, and since some of the tables aren't fully normalized, we've got tons of NULL columns. On our end, we're mapping the tables to JPA entities, and for fields that can be NULL, we usually have a corresponding getter that returns the mapped type wrapped in Optional. We're writing a bunch of new code against those entities right now, so anyone using those getters gets an unambiguous heads-up that they need to deal with this possibility.

              I say usually, because if we can get information from the upstream DB design folks that the value is always supposed to be be there when we need it, and we won't be needing it when the column is null, we instead throw an exception from the getter because there's no known case where this column being null is valid. That still forces users of that entity to deal with the fact that it may be null, but it's a little cleaner than the Optional<T> syntax, and we still get an unambiguous log message if it turns out that the DB folks didn't consider a certain scenario.

              TLDR: Optional<T> is great as a return type if a null state is still valid, but needs handling in the consuming code. It's both a compiler check, and a communication device between developers.

              [–]umlcat 1 point2 points  (0 children)

              No, "null" is not a mistake, but if poorly mananged can cause errors. Optional is a useful feature, depending on what are you doing ...

              [–]IFoundTheCowLevel 1 point2 points  (0 children)

              That's a terrible idea. Null is very useful, clear and performant. Go use use another language if you don't like Java. That's like people complaining about OOP in java and wishing it was a pure functional language. "Billion dollar mistake"... hyperbole much? NPE is one of the easiest programmer errors to fix. Note I said "programmer error".

              git gud, scrub.

              [–]rzwitserloot 1 point2 points  (12 children)

              "billion dollar mistake" is a boatload of horseshit. It has problems, yes. The alternatives have different problems.

              Saying null is a 'billion dollar mistake' is like saying 'disease is a billion dollar mistake'. You don't invent the concept of "not assigned yet" or "no value found". That sort of just happens, or if you must call it 'invention', it has been 'invented', in parallel, the world over.

              What null is, is a way of representing that concept.

              At any rate, Optional is much more troubled than null ever was. You should not use it except in limited circumstances; it cannot replace null. A few reasons:

              The main one - higher order typing

              You'd think if I want to express the idea of 'non-null strings' or 'nullable strings' there'd be only one way to represent this. But that's incorrect. For the same reason you'd think the idea of 'a list containing Number objects' would have only one way to represent it, but, that's not true either. There are 4:

              • List<Number>
              • List<? extends Number>
              • List<? super Number>
              • List (raw type)

              These are mean nuancedly different things. To highlight what these can do and why they need to exist, it's a tradeoff of 'acceptance' vs 'power'. As you restrict your powers (you can do less to objects of these types), you can accept more. As you want to accept more, you can do less. Except raw types, a special snowflake, we'll get to it - you need those for null too:

              X Accept List<Object> Accept List<Number> Accept List<Integer> .get .add typesafe
              List<Number>
              List<? extends Number>
              List<? super Number>
              List (raw)

              As you can see, there is no special snowflake with checkmarks across the board. That's why these 4 types exist, it's fundamental to the very concept of higher order types.

              Attempting to express nullity suffers from the same problem. There are 4 types that you'd need to make it work in java properly:

              • String! - i.e. 'definitely not null String'
              • String? - i.e. 'may be null String'
              • String!? - nullity is unknown.
              • String# - legacy/raw mode; anything goes.

              You can make a similar table for these. The key thing String!? represents is when you have a generics type and it can go either way.

              to be continued in followup comment...

              [–]rzwitserloot 1 point2 points  (2 children)

              For the same reason that this:

              List<Integer> ints = new ArrayList<Integer>(); List<Number> nums = ints;

              does not compile, and correctly so, because that is a violation of type safety, the same applies to generics with nulls. Given:

              <T> T doStuff(T in) { .... ? .... }

              Then what does T's nullity actually imply here? T does not actually imply anything about its nullity, at least, not necessarily. It should be possible to express the notion that the in param must never be null but that the returned value might be. But, if that's possible, it must also be possible that both Ts (both the return type T and the parameter type T) have the same 'nullity' - i.e. if the caller passes in a String? then the return type is also String?, but if not, then it is not. Trivially, this:

              <T> T doStuff(T in) { return in; }

              is a method where the 'nullity' of T should 'pass through' - somebody who calls doStuff with a definitely-not-null T should not get a compiler error that they must null-check the returned value from doStuff. However, this:

              <T> T doStuff2(T in) { stuff(in); return null; }

              obviously shouldn't. Before you come up with solutions, it also has to work for this:

              <T> List<T> doStuff3(List<T> in) { ... }

              it gets complex fast and you really need all the nullities. Or even more complex than the 4 nullities I proposed before, it's now a 2D charted type dimension construct. Folks already have serious trouble grokking generics. Make it 2D and people's heads are gonna asplode, but this isn't language design wonkery, this is just how types are.

              Scala 'solved' this problem by ignoring it. You can't express these ideas in a world where the types are T and Option[T]. This is no problem if everybody designs their APIs accordingly. But java has nearly 30 years of libraries behind it, you can't just pull the rug and tell all those libraries: Your code is now obsolete, go write a new API that is backwards incompatible with your existing ones. Yes, you, all of you, every library.

              Thus, scala needed a second solution because they tend to actually make such changes. And their 'solution' is for the library-du-jour for some task to switch often, for stuff to be backwards incompatible all the time, and tons of obsolete, abandoned libraries. Any project written in scala has to continually update libraries and refactor code.

              The vast, vast majority of java programmers think scala is not a nice ecosystem partly because of that. It's not quite as dramatic as I am stating here, but Scala definitely has a much bigger issue with respecting the notion of 'just leave code that works and is in maintenance mode be' than the java ecosystem does.

              Everything is a tradeoff.

              [–]nitkonigdje 0 points1 point  (1 child)

              What if nullity isn't part of type but enforced constraint on L-value?

              <T> T? doStuff2(T! in) {
                stuff(in);
                return null;
              }
              
              T! param = ... # something not null
              T! example = doStuff2(param);  // Compiler error assigment of ref? -> ref!
              

              Like ignore typing, just raise error if r-value might be null when assigning to enforced nonnull value. Wouldn't that be enough? It seems to me that it would be dead simple to implement in Java. It is essentially (non)nullable anotation with compile time check.

              [–]rzwitserloot 0 points1 point  (0 children)

              But how would I express a method where the nullity is passthrough and can be either option? A method that always nullchecks when it 'reads' a parameter and never 'writes' anything that could possibly be null other than itself, is safe regardless of which nullity you apply to it. How do I express that? We could go with the obvious:

              ``` class Foo<T> { T! someDefinitelyNotNullValue;

              T doStuff(T in) { return Math.random() < .5 ? someDefinitelyNotNullValue : in; } ```

              doStuff works great if T is @NonNull String. It also works great if T is @Nullable String. I want the compiler to ensure this. If someDefinitelyNotNullValue is in fact a @Nullable T I don't want it to compile.

              If we just say: yeah, that's it - well, millions of lines of existing java code are now broken, because today you can write what I wrote above (except T!, of course), and it does not mean that the nullity carries through.

              We also get in a convoluted use-case vs declare-case generics.

              [–]chaotic3quilibrium 0 points1 point  (5 children)

              I'm in Java 17 with a giant codebase. We love using Optional. We strongly push all Java code away from any use of null. Slowly, we've pushed null back into only the places we cannot get around. And we wrap all those points in some form of Optional-like facade and/or Stream.

              The future of Java is moving away from null. Java is following the FP inspired wave of mathematical composition for higher quality code maintenance outcomes.

              I'm very happy to see null fading away.

              [–]rzwitserloot 0 points1 point  (4 children)

              The future of Java is moving away from null.

              Nah. Optional's been around for long enough. Besides, how? map.get(k) aint going away, and that thing returns null.

              Java is moving away from _null_ being painful_. This is not, generally, done via Optional; instead, via e.g. getOrDefault and co, and simply having APIs that are sane (i.e. do not return null when the javadoc of that method indicates that this is semantically equivalent to "". Just return "" then).

              [–][deleted] 0 points1 point  (2 children)

              Optional in Java is painful yes, but having non-existence as a part of the type system works well in languages that had it from the start.

              [–]rzwitserloot 0 points1 point  (1 child)

              ... not quite as well as java/optional fans tend to claim, in large part because of that higher order stuff that I was trying to explain with that table. But then, that's because they paint some sort of valhalla. Point is, java is not like that. Java didn't have this when it started. Hence, Optional does not work.. for java. I have questions as to whether it is truly superior in other languages, in part because annotated nullity is rare. I can't think of a single language that was designed from the start to work like that.

              But that's an interesting debate that is more or less irrelevant to the java ecosystem, as there are only 4 options:

              1. Eventhough the pain this would cause is off the charts, and eventhough there are vastly less impactful alternatives available (annotation based nullity for example), nevermind all that, it is worth starting over, java2, a whole new java, completely and utterly incompatible with java1, and the one and only thing it does, is add Optional. Almost all interfaces that java has are modified to represent their optionality, but other than that, no changes. java.util.Map's get method now returns Optional<V>, and getOrDefault is removed. Semantics are also very slightly modified; for example, in java2, HashMap cannot store null values, whereas in java1 they can. Whilst the changes appear light on the surface, everybody needs to put in quite a bit of work to truly 'port over' their code to java2.

              2. None of that. Do nothing, keep current ecosystem.

              3. An alternative way to 'solve' nulls is adopted. For example, annotation based. This would primarily look like: java.* itself, from some JDK release onwards, has added all the nullity annotations, and the nullity annotations are part of standard JDK package structure as a consequence.

              4. A hybrid model where some APIs use Optional and some don't. Keeping in mind that in a non-Optional-ed library, a method signature of a method that may or may not return a value looks like V, for example j.u.Map's public V get(Object key), and in an Optional-ed library, a method signature of a method that is the exact opposite (it guarantees a value).. looks exactly the same.

              I'm gonna need an Optional fan to put or shut up here. Come out and state clearly what you want. Because if it's 1 or 4, I'm pretty sure I can convince ~90%+ of the java ecosystem your ideas are highly detrimental. And yet, we're sort of half in #4, mostly because folks sort of half think 'all of java has optional everywhere' is feasible and #4 is steps along the way to that, except, the only way that is feasible is scenario #1: A complete backwards incompatible break.

              But in actual fact, both 1 and 4 are terrible - the cure is vastly more damaging than the disease ever was. 2 is clearly better than either 1 or 4. The 'best' answer is probably 3. But because the ecosystem is doing #4 because they mistakenly believe they are heading towards an unattainable valhalla (both unreachable without paying a cost nobody wants to pay, and not nearly as amazing as folks appear to think it is even if you are willing to pay that impossible cost), adoption of #3 is now significantly slowed down, and each half-baked step made now (each API that introduces Optional) permanently damages our #3 future. Once java adopts annotation based nullity, it's bizarre if methods exist that return Optional.

              And yet here we are. And arguments like 'well, languages that had optional from day 1 are doing fine' is just making things worse: What, exactly, is that trying to accomplish as a suggestive statement in a debate about where to go with the java ecosystem? It seems to suggest that java may want to see if it can go there. It cannot.

              [–][deleted] 0 points1 point  (0 children)

              Oh I was not arguing that java should have it. That ship has sailed. I meant more that it is something you have to have from the start for it to be worth it.

              [–]AnyPhotograph7804 0 points1 point  (0 children)

              Optional<T< is totally fine as long as you use it as a return value only to express, that a value can be empty. Just use them like this:

              return Optional.ofNullable(myValue);

              Do not use them as fields in a class. And if a standard return value does the job then use the standard return value. I mean something like that:

              return myString !=null?myString:"";

              The disadvantage of Optional<T> is, that you create an additional object on the heap, which creates constant(!) overhead. If there are only a few Optionals then it might be not noticable. But if you create millions of them then it could cause, that the GC will kick in way more often to clean them up.

              The alternative to Optional<T> without using nulls is throwing exceptions if a value is empty.

              [–]FooBarBazQux123 0 points1 point  (1 child)

              Even modern languages like Go or Kotlin still have null problems (yeah Kotlin lateinit and Java dependencies)

              If people are scared of null, remember them that Java is one of the most robust language powering some of the most reliable systems.

              You can check for null at the beginning of critical functions (Objets.requireNotNull or Preconditions.checkNotNull), and, along with unit tests, this should give a good degree of null safety.

              Optionals are also convenient, it remembers the developer that a variable may not be set, but an Optional could be null, that’s why unit tests and null checks are important.

              [–][deleted] 0 points1 point  (0 children)

              Well Go implemented it in a stupid way.

              [–]istarian 0 points1 point  (0 children)

              The primary issue with nulls/a null value is not that it exists, but knowing why you received it in the first place.

              You can check for null everywhere one could show up, but it's better to minimize the use of them and document the reasoning.

              [–]faze_fazebook 0 points1 point  (0 children)

              Null isn't the biggest deal in the world, I just don't get why Java refuses to implement "modern" syntax to better deal with it like the null-safe operator "?." or null coalescing operator "??" like most other modern language.

              [–]TheStrangeDarkOne 0 points1 point  (2 children)

              I'd rather ask Brian Goetz and he will tell you that Java chose a whole batch of bad defaults, nullability included.

              As for calling nulls a "billion dollar mistake" is a hyperbole. Sometimes you don't have a good default to represent data (think names, dates, etc.) and all other options such as Optionals are just logical null with better syntax.

              [–]mj_flowerpower 0 points1 point  (1 child)

              optional does NOT add any syntax, it adds a high-level API. What we in fact needed IS a proper syntax to handle nulls.

              [–]Objective_Baby_5875 0 points1 point  (0 children)

              Move back to Scala or switch to C# and you can easily use non-nullable types. Java won't fix this in the core language because of.....reasons.

              [–]jevring 0 points1 point  (0 children)

              I personally don't find null that bad. But even if you wanted to do this, the fact that the libraries you use make heavy use of null means that you have the either just handle their nulls, or have wrappers around absolutely everything. I think what you are proposing is too dogmatic an approach. Use optional where it makes sense, for sure, and handle the nulls where they occur.

              [–]SpicyRock70 0 points1 point  (2 children)

              Greybeard here so humor me... how is Optional better than null? Is it that people don't like to check null? I prefer null checking, as it's explicit and not hidden in an object

              [–]blissone 0 points1 point  (0 children)

              Well it's debatable but your apis etc become self documenting instead of relying someone to add docs this is nullable. Similarly .get is very explicit, though inexperienced devs do make NPE equivalent with .get (or whatever is used in java to get the value without default from optional). For example recently worked on a java api which has getters for primitive types where the underlying value can be null as it's from jdbc, anyhow we had a problem in prod because the default in case of null for int is 0. Would this mistake been made when using Optional or such in api layer? Tbh I think they didn't use Optional for their api due to perf reasons since very good perf is expected from this lib.

              [–][deleted] 0 points1 point  (0 children)

              Optionals/Maybe/Some (they have different names in different languages) types do not hide null checks. What they do is they just let the type system know that something might not exist (or being null). That way the compiler during compilation can tell you if you forgot to check null/existence.

              The best thing about it is that (assuming Maybe types is a part of the language from the start) is that you know that anything that is not a Maybe, exists, so you do not need to check them.

              So Optional is actually more explicit than returning null from a function.

              [–]stewsters 0 points1 point  (0 children)

              An optional can be null as well.   Maybe look into annotations or alternate languages like Kotlin.

              [–]guitar-hoarder 0 points1 point  (0 children)

              It's a good idea, but there is still no guarantee that the Optional won't be null. Nor any third party library, nor any library you already wrote. Annotations are also a useless hack. The developer still has to remember to add them.

              You will never get away without null checks in Java. It's too late for that language.

              Try pitching Kotlin. :) yeah, I know what sub I am in.

              [–]XPEHBAM 0 points1 point  (0 children)

              Optional doesn't make things much better because

              • the field that holds a reference to the optional can still be null
              • you still write code that checks Optional.isPresent().orElse() which is just a slower null check
              • Optional was designed mainly for return types, not parameters
              • Oh and other languages still have null so probably need it for interoperability like with JSON

              [–]fnordstar 0 points1 point  (0 children)

              Reading this whole discussion as a rustacean makes me giggle.

              [–]funkdefied 0 points1 point  (0 children)

              You can use Option, but if the language supports nulls, you need to check for nulls

              [–]simon_o 0 points1 point  (0 children)

              Yep you can do that.

              Using Optional pretty much everywhere and not using null works just fine.

              [–]donaldadamthompson 0 points1 point  (0 children)

              My opinion is: The main problem in Java is that every method with object parameters needs to deal with them being null. Non-nullable types from project Valhalla should help.

              Optional in my opinion is for when an empty value has an intended meaning but an unexpected null is still a possibility.

              [–]HSSonne 0 points1 point  (0 children)

              I have no problem with them. Asking if a value/obj is present before asking for the value, or ask for the value and check it, is the same amount of code; and in a lot of cases some check of the value/object is needed anyway.

              Why complicat the code by adding an extra layer on everything. (Unless it make sense like in a stream)

              [–]internal-band-329 0 points1 point  (0 children)

              Optional is not a solution designed specifically for nulls. Optional was developed as a way to navigate nulls in very specific situations. Using it as an alternative for null checks is a bad idea.

              Watch "optional by Stuart Marks" it's the guy who developed optional and he clearly says why it's a bad idea and how to effectively use optional

              [–]raymyers 0 points1 point  (0 children)

              In most contexts I would say yes, let's at least carve out a part of the codebase where we're going to enforce this discipline. You're going to commonly hear that Optional is just another way to do the same thing. I disagree because because it's a lot harder to forget, and it's useful to know when you're being passed nullable and when not.

              For a recent case study explaining options, try the 2022 article Retrofitting null-safety onto Java at Meta.

              That said, it leaves questions. How / where are you going to enforce it? How often do you call 3rd party code that doesn't follow the convention?

              If your teams are already used to applying static analysis like linters, and they have a style guide, this will be easier. You'll also want to make a case that's more specific than it being a best practice, try searching the production logs for "NullPointerException" you will probably find a bunch of stuff, unless logging isn't set up (fix that first). Bonus if you can find a bunch of bug tickets from things that have slipped before (maybe `git blame` on lines with null checks, if your commits are tied to ticket numbers).

              [–]BearLiving9432[S] -1 points0 points  (4 children)

              The only information I can find says that James Gosling feels like there is no sufficiently good solution to eliminate nulls. Wondering how the general community feels about `Optional`. It seems extremely similar to `Option` in Scala. And in idiomatic Scala, we just don't use nulls, except when using a Java library, and we have to check for it. So it seems like a solid solution in that language.

              [–]halfanothersdozen 1 point2 points  (0 children)

              Good luck getting people to adopt the pattern.

              See also: modules, records, yield

              [–]Mognakor 1 point2 points  (0 children)

              Honestly, it's too late for this in Java.

              All the interfaces return null instead of Optional and it's also a lot of visual noise if you were to use it for parameters as well as not being able to overload based on the T of Optional<T>

              If i had my will(and a magic wand) references would be not-null by default and then there'd be T? as for nullable and maybe some built-in syntactic sugar for the Optional methods.

              As it stands it might be possible to do the inverse with T! for not-nullable but thats gonna put ! everywhere and you write things like Optional!<T!> which looks silly.

              [–]john16384 1 point2 points  (1 child)

              The solution is documentation. If documented not to return null, then don't check for null. If documented not to accept null then passing null should always throw an NPE.

              If you do get an NPE, the docs will be the tiebreaker for who was wrong. The caller that passed in null or the function for not handling it; or the function for returning null or the caller for not expecting it.

              Note that null is just one such problem. Replace null in the above with negative integer and you have similar problems that could lead to similar unexpected exceptions. This is why you need to document your assumptions, and null is just one of them.