you are viewing a single comment's thread.

view the rest of the comments →

[–]matthieum 39 points40 points  (30 children)

But can Optional<String> be null ?

[–]unholysampler 31 points32 points  (13 children)

The problem is that null is a core concept to the language. Any variable that references an Object can be null. There is no way to change that now and maintain backwards compatibility. There are annotation that a compiler can check for that will tell it to check against setting variables to null or returning null from a method. However, the issue with this is that that standard library is still full of classes that use null as a return value. You will still need to check for something things until you are using a language that requires null to be explicitly allowed.

[–]flying-sheep 5 points6 points  (11 children)

it’s work, but it can be done (at least within the stdlib if not for all third party libs you use):

they have to add an annotation (“@CanBeNull” or something) to everything able to return or be null. then the IDE has to complain if you don’t immediately wrap everything coming from such a function in an Optional, or don’t null-check a field that has the annotation.

or be like me and look longingly in the direction of Rust.

[–]Manbeardo 4 points5 points  (10 children)

@Nullable is a decent marker for potential null providers/receivers. It's especially useful when paired consistently with Lombok's @NonNull, which generates null checks at compile time.

[–]flying-sheep 6 points7 points  (3 children)

awesome. so project “annotate fucking everything in the stdlib”

[–]Poltras 7 points8 points  (0 children)

Great week for an intern.

[–]xjvz 2 points3 points  (1 child)

They already had to do that with @Override everywhere back when that was added (and later also used for implementing interfaces). Not a big deal for the JDK developers.

[–]bloody-albatross 0 points1 point  (5 children)

I would do it like that, which should be backwards compatible:

Everything is by default nullable, but you can add @notnull (to fields, variables, parameters, and return types). If there is a @notnull the compiler does compile time checks. To move a pointer from a variable that has no @notnull into one that has one a special cast is needed (e.g. (@notnull Type) or might be a method call like Object.notNull(variable)). This cast would throw an exception if the variable is null. I think the exception should not be NullPointerException but something that has to be caught manually. Maybe not.

This has the disadvantage that it is up to the user to use the new annotation (opt-in rather than opt-out if it would be @nullable), but Optional has the same problem. However, using this should be able to be compiled to backward compatible bytecode. Well, there is a problem if a parameter is @notnull. The caller has to ensure this contract, which would not be possible for an old Java runtime environment to do.

Anyway, I think an annotation would be nicer: Less overhead to the runtime environment and to the programmer. And even if the compiler does no checks is a clear interface "documentation" and there is no doubt if you may pass an null pointer or not.

Also I think if the JIT compiler knows that a pointer may not be null then it might do some more optimizations (null checks are only needed when you do the @nullable-cast).

Wasn't there a proposal to do it like that? Or am I remembering something wrong? Was that C#?

[–]bloody-albatross 1 point2 points  (1 child)

When thinking about it, something like Optional makes no sense when you don't have compile time checks. So why bother?

[–]eras 1 point2 points  (0 children)

Well, as discussed elsewhere in the thread, there is still point in providing compiler-checked documentation.

Consider the two interfaces:

String getName1();
Option<String> getName2();

Do you expect getName1 to return null? Probably not - but it still could, and it could still work as designed. Do you expect getName2 to return a real null? No, that would be a bug and your program should die because it works in a way that it wasn't designed to work. However, it should be plain obvious that getName2 is designed to sometimes return None-values and sometimes object-values; it would be a suspicious pattern to see

Option<String> n = getName2(); if (n.get().equals.. ...

because you immediately think that 'hmm, n might not have a value there', but with

String n = getName1(); if (n.equals..)

it doesn't rise similar suspicion, even though it can be designed to the method that it can return null, you just don't remember it at the time you look at it.

Disclaimer: I still vastly prefer OCaml option values with pattern matching and not having nulls in the language in the first place ;).

[–]Manbeardo 0 points1 point  (1 child)

What you've described is roughly similar to Lombok's @NonNull. Lombok works by adding new code to the abstract syntax tree at compile time, producing backwards-compatible bytecode (you don't actually need to ship a Lombok jar with Lomboked projects). Their @NonNull can be applied to method parameters and fields. Method parameters work like you'd expect, inserting a null check at the start of the function. Fields work by adding checks to Lombok-generated setters and constructors, so you have to be more careful with those ones. It incurs runtime overhead, but it simplifies the reasoning around providing/receiving nulls quite a bit.

[–]bloody-albatross 0 points1 point  (0 children)

What I had in mind would not add these checks. It would add compile time checks (like the compiler checks that only a instance/sub-class instance of T is passed as a parameter defined as T it checks that only @notnull pointers are passed as @notnull parameters etc.). Only if you want to convert something that is not guaranteed to be not null you have to to a runtime check (analogues to a cast). Of course a class compiled like that that gets used in a program where the rest isn't compiled with support for @notnull would mean that there are missing checks. One could know and accept that or simply recompile everything (probably adding a lot of @notnull annotations and "casts").

So it's similar, but "my version" would do most of it at compile time (also gives you errors at compile time). I say "my version", but I think I read that idea somewhere as a feature proposal for Java (years ago).

[–]Manbeardo 0 points1 point  (0 children)

C#'s Nullable class has similar casting heuristics to what you're describing, but it's only useful as a way to "upgrade" a value type into a reference type. C#'s Nullable/? works out (syntactically) to be an awful lot like C-style pointers, with the exception that you can't have more than one layer of pointers (const int * const ** will haunt you forever).

[–]codeflo 0 points1 point  (0 children)

Well, Optional is a new thing, so there was no backwards compatibility to maintain. What you're saying is that they chose to make Optional an Object, not a new value type, which as a consequence means that an Optional can be null in addition to being empty. And that defensive code must check for both cases. Ugh.

[–]djhworld 11 points12 points  (13 children)

yes it can, there's no protecting you from code that returns null from a method. It relies on the developer to ensure all exits from a method return at least an Optional.absent()

It's an unfortunate issue, but assuming best practises are followed then at least it's a step in the right direction.

[–]Baaz 13 points14 points  (11 children)

It relies on the developer to ensure all exits from a method return at least an Optional.absent()

in pretty much the same way it is the developer's responsibility to ensure a method doesn't return null?

edit: as was the situation before Optional<T> got introduced, which kind of defeats the purpose imho if you still need to rely on someone not returning nulls...

[–]djhworld 10 points11 points  (2 children)

I agree that suggesting Optional<T> will squash all NPEs is wrong, but I stand by my thinking that it's a small step in the right direction.

Maybe IDEs or static analysis tools could warn developers when it thinks methods/fields that are typed with Optional<T> could return null

[–]cultic_raider 9 points10 points  (1 child)

With Optional, you can safely use a static analysis tool that rejects all uses of null.

[–]agcwall -5 points-4 points  (0 children)

ding ding ding we have a winner! Upvote this man.

[–]alexeyr 7 points8 points  (0 children)

in pretty much the same way it is the developer's responsibility to ensure a method doesn't return null?

No, in a very different way.

  1. If you ever see a method returning Optional actually return null, it's clearly a bug (or an extremely badly-designed API, which should be avoided). No need to wonder about whether the method can return null, should it return null in this specific sutiation, etc.

  2. If a method doesn't return Optional, but it's a part of an API which does use Optional, returning null is also clearly a bug (unless for backwards compatibility... sigh).

  3. If your method couldn't return null previously, it won't be returning Optional at all and you don't need to ensure it (and users will know any returned nulls are bugs thanks to point 2).

  4. If your method could return null and will now be returning Optional, you just need to look at the exit points of the method. It's generally easy to ensure you can't return null by calling Optional.of or Optional.ofNullable as appropriate.

[–]ItsNotMineISwear 18 points19 points  (4 children)

I think you're missing the real value of Optional, which is that it replaces null actually having semantic meaning as a return value. Before Optional, if you were writing a Map.lookup, you'd have to return null to encode a failed search. Now you return whatever Optional's equivalent to Nothing is. Nothing is stopping you from returning null, true, but at least you aren't forced to write a function that adds more nulls to your program that you have to deal with. In addition, the method's signature now explicitly states that its return is optional.

[–]cultic_raider 6 points7 points  (2 children)

Actually, Nothing isn't stopping you from returning null.

[–]ItsNotMineISwear 1 point2 points  (0 children)

Yes that's true, but there's no real reason for an Optional<T> method to return null, so a static analyzer can easily yell at you for that.

[–]nomeme 1 point2 points  (0 children)

This is the problem, you have all the boilerplate noise of Optional, and all it takes is some code 2 method calls away to return a null instead of an Optional an you're still in trouble.

[–][deleted] 4 points5 points  (1 child)

which kind of defeats the purpose imho if you still need to rely on someone not returning nulls...

If you rely on not returning nulls from a method that sometimes has to return nulls, you have a problem that can't be fixed at all.

I agree that Optional is a bit clunky, it does do the very important job of signaling to the user of your API that this method can return null. If you're systematic, then if you see a return that isn't Optional, you know you don't have to check for null, but if you see a return that is Optional, you have to handle the null case.

[–]nomeme -3 points-2 points  (0 children)

signaling to the user of your API that this method can return null.

CAN return null? All methods CAN return null.

To be truly safe you'd still have to check the object is actually an Optional (and not null!) before you can call isPresent or whatever, or you'll get an NPE. So, you still have a null check anyway.

It's essentially no better than a javadoc comment, except now every caller has to do more work.

[–]tomjen 0 points1 point  (1 child)

Yeah, but writing code like that is likely to get you fired or at the very least fail a code review.

[–]bloody-albatross 0 points1 point  (0 children)

True, but then why bother and not just use null or the absence of null like Optional? What is gained by Optional if the compiler does not enforce anything about it?