632

I've read on many Web sites Optional should be used as a return type only, and not used in method arguments. I'm struggling to find a logical reason why. For example I have a piece of logic which has 2 optional parameters. Therefore I think it would make sense to write my method signature like this (solution 1):

public int calculateSomething(Optional<String> p1, Optional<BigDecimal> p2) {
    // my logic
}

Many web pages specify Optional should not be used as method arguments. With this in mind, I could use the following method signature and add a clear Javadoc comment to specify that the arguments may be null, hoping future maintainers will read the Javadoc and therefore always carry out null checks prior to using the arguments (solution 2):

public int calculateSomething(String p1, BigDecimal p2) {
    // my logic
}

Alternatively I could replace my method with four public methods to provide a nicer interface and make it more obvious p1 and p2 are optional (solution 3):

public int calculateSomething() {
    calculateSomething(null, null);
}

public int calculateSomething(String p1) {
    calculateSomething(p1, null);
}

public int calculateSomething(BigDecimal p2) {
    calculateSomething(null, p2);
}

public int calculateSomething(String p1, BigDecimal p2) {
    // my logic
}

Now I try writing the code of the class which invokes this piece of logic for each approach. I first retrieve the two input parameters from another object which returns Optionals and then, I invoke calculateSomething. Therefore, if solution 1 is used the calling code would look like this:

Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result = myObject.calculateSomething(p1, p2);

if solution 2 is used, the calling code would look like this:

Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result = myObject.calculateSomething(p1.orElse(null), p2.orElse(null));

if solution 3 is applied, I could use the code above or I could use the following (but it's significantly more code):

Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result;
if (p1.isPresent()) {
    if (p2.isPresent()) {
        result = myObject.calculateSomething(p1, p2);
    } else {
        result = myObject.calculateSomething(p1);
    }
} else {
    if (p2.isPresent()) {
        result = myObject.calculateSomething(p2);
    } else {
        result = myObject.calculateSomething();
    }
}

So my question is: Why is it considered bad practice to use Optionals as method arguments (see solution 1)? It looks like the most readable solution to me and makes it most obvious that the parameters could be empty/null to future maintainers. (I'm aware the designers of Optional intended it to only be used as a return type, but I can't find any logical reasons not to use it in this scenario).

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
Neil Stevens
  • 6,745
  • 4
  • 14
  • 7
  • 27
    If you used optionals, wouldn't you have to check that the optional passed as a parameter isn't `null`? – biziclop Aug 10 '15 at 14:59
  • 41
    Yes, but it would make it obvious to some one else maintaining the code in the future that the parameter can be empty/null, therefore potentially avoiding a null pointer exception in the future – Neil Stevens Aug 10 '15 at 15:05
  • 1
    Wow. Null arguments being passed into a method? That's so Visual Basic. What principle is it violating? SRP (maybe). It also violates another principle whose name leaves me bereft goes along the lines of passing in ONLY the necessary information for a method or function to do its job. – Luminous Aug 10 '15 at 15:12
  • possible duplicate of [Uses for Java8 Optional](http://stackoverflow.com/questions/23454952/uses-for-java8-optional) – Didier L Aug 10 '15 at 16:05
  • If libraries start to frequently return Optional and then we use Optional as parameter of other methods, don't we end up to have just Optional type everywhere? I don't understand. – Niccolò Apr 21 '16 at 14:06
  • 1
    @NeilStevens You forget that a reference of type `Optional` can itself be `null`. – jub0bs Jul 01 '16 at 15:33
  • 37
    Everything theoretically possibly being null is like every method in a library possibly calling System.exit(0). You cannot check against tis and you shouldn't check against this. Everything you would have to do all the time you in fact should (almost) never do. Like making all parameters final. Let your tools help you prevent changing parameter values or forgetting to initialize fields instead of making your code unreadable by thousand finals and as many null checks. – yeoman Aug 19 '17 at 14:57
  • 12
    Actually just use the NonNull/Nullable annotations, that's what you're looking for in this situation, not optional. – Bill K Jan 30 '18 at 23:28
  • 5
    don't really understand the argument that Optional<> can be null itself. If you stablish that in your codebase there are no nulls (in every boundary with external libs you ensure that), then you can rest assured there are no nulls. I worked in a codebase like that for 2 years and we never got a NPE. Now in a codebase that wants to use solution 2 we get NPE every couple of weeks, so it can't be better, sorry. I vouch for solution 1 every single time. Scala does the same as well and no one thinks about nulls. And I think kotlin too – pedrorijo91 Feb 25 '20 at 07:02
  • An argument can be made that Optional is a hack to work around the lack of type unions in Java (and similar classes, in other languages). This might be of interest: https://www.youtube.com/watch?v=YR5WdGrpoug If you did, the Optional passed to a method could still be null, so you'd have to check that too - making the code even more complicated. And if you started non-optional T and widened the method to allow Optional, you've broken all your callers by changing the type. – Paul May 11 '22 at 12:37

26 Answers26

334

Oh, those coding styles are to be taken with a bit of salt.

  1. (+) Passing an Optional result to another method, without any semantic analysis; leaving that to the method, is quite alright.
  2. (-) Using Optional parameters causing conditional logic inside the methods is literally contra-productive.
  3. (-) Needing to pack an argument in an Optional, is suboptimal for the compiler, and does an unnecessary wrapping.
  4. (-) In comparison to nullable parameters Optional is more costly.
  5. (-) The risk of someone passing the Optional as null in actual parameters.

In general: Optional unifies two states, which have to be unraveled. Hence better suited for result than input, for the complexity of the data flow.

Anurag
  • 353
  • 3
  • 15
Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • 72
    Actually, having an `Optional` parameter represents one of three states: a `null` Optional, a non-null `Optional` with `isPresent() == false` and a non-null `Optional` wrapping an actual value. – biziclop Aug 10 '15 at 15:43
  • 26
    @biziclop yes, an unavoidable point already criticized. But the _intention_ is to have non-null expressions only. That it did not take long, to hear the advise to avoid Optional in some cases too, is quite ironic. A `@NotNull Optional`. – Joop Eggen Aug 10 '15 at 15:53
  • 117
    @biziclop Note that if you're using `Optional` at all, then state 1 (`null` Optional) usually indicates a programming error, so you might as well not handle it (just let it throw a `NullPointerException`) – user253751 Sep 25 '15 at 08:07
  • @immibis I agree but it would be nice to have some compiler-time guarantee (as much as a compiler can give at least, if someone nulls out an `Optional` variable via reflection or deserialization, that's tough luck) about optionals. Without that guarantee its use is fairly limited. – biziclop Sep 25 '15 at 08:55
  • 123
    "suboptimal for the compiler", "In comparison to nullable parameters Optional is more costly" - these arguments could be valid for C language and not for Java language. Java programmers should focus on clean code, portability, testability, good architecture, modularity, etc., and not on "Optional is more costly that null reference". And if you find that you need to focus on micro-optimizations, then you'd better skip Objects, Lists, Generics and switch to arrays and primitives (I don't want to be offensive here, I'm just sharing my opinion). – Kacper86 Nov 09 '16 at 11:31
  • 4
    @Kacper86 thank you for stating that, as I mostly avoid nulls in favor of Optional. However there are some "clean-coders" evidently that argument to avoid Optional as input parameters, and I wanted to list all down to earth pluses and minuses, **without judgement**. I think that is important as we still have not much experience. 3. `f(Optional.ofNullable(x))` reeks a bit, but 4. is indeed the C attitude of "I do not need to earth my electric device." – Joop Eggen Nov 09 '16 at 20:17
  • 2
    I think this list is not really helpful: using the words "contra-productive" is not descriptive enough to explain the issue. Also checking null is the same amount of code, so I don't see what's contra-productive more than the obvious solution. Second, considering compiler level optimizations in design is not the correct approach when describing your API. – Elad Tabak Nov 29 '16 at 08:26
  • @EladTabak several commenters and also you convinced me that this question would have been for some other forum (CodeReview?) and dealt in more detail. We would probably share some points. – Joop Eggen Nov 29 '16 at 13:30
  • 2
    "Using Optional parameters causing conditional logic inside the methods is literally contra-productive." - bad argument. map and flatMap is the FP way and no conditional logic is introduced. – user239558 Feb 20 '17 at 14:58
  • @user239558 thanks and java 8's `Optional` still is lacking some stream features (see java 9). Though I would not be so categorical, especially for non.FP objects passed around. – Joop Eggen Feb 26 '17 at 21:43
  • 37
    "suboptimal for the compiler": Premature optimisation is the root of all evil... if you aren't writing performance critical code (which is often the case when trying to specify a clean interface), this shouldn't be a concern. – Mohan Nov 07 '17 at 19:58
  • 2
    @Mohan I _do_ love using Optional all the time, especially when doing old code. However for the question's context it seems a valid argument, as it is not premature optimisation using `int` instead of `Integer`. Doing an `f(Optional.ofNullable(x))` falls a bit in that int/Integer category of unneeded boxing/unboxing, wrapping/unwrapping. As I said I am a big fan of Optional, so thanks. – Joop Eggen Nov 08 '17 at 00:17
  • 4
    I have come to hate Optional. Most of its uses are completely unnecessary. I would add one more guideline: Only use optional where the value can actually be null. I've seen it used on getters for constructor-injected members that are final and can't possibly be null. Before Optional came along, I saw code with a lot of unnecessary null checks. Optional has only made it worse. – MiguelMunoz Oct 03 '18 at 22:24
  • 1
    I feel this does not provide a solution but only criticism. – Hangfei Lin Jan 19 '19 at 07:22
  • 5
    I don't get the compiler related oppinions. So, if you program in Java, you won't bother about optimizations, that's all you are saying? Well, that may be the case for front-end or typical java apps. But Java is often used for critical systems, big data processing, real time transforms, etc. In that case, YOU DO NEED optimizations, and yes, it makes a HUGE difference on those systems. If you just make fancy interfaces, front-end apps or so on, maybe you don't mind about Joop Eggen's recommendation about compiler. If you make more serious things, you DO mind. Don't generalize a whole language – aran May 13 '19 at 10:51
  • Note calling any method that returns an Optional will make point 3 void. You already have the Optional object created. – borjab Mar 19 '20 at 12:59
  • #2 is incompletely stated or invalid. If arguments can be sometimes null and sometimes not then the method will likely *have* to include conditional logic for the two cases unless it's forwarding the arguments to other methods. Use of Optional vs null changes nothing in this regard – Hoobajoob Oct 20 '22 at 23:56
  • @HangfeiLin thanks, though optional things are very real, and just using null then seems understating the importance of characterizing the optionality. And also the expressiveness of mapping to an (optional) field with just `map` I have come to like. But you might be right about the stating of #2. – Joop Eggen Oct 21 '22 at 06:48
221

The best post I've seen on the topic was written by Daniel Olszewski:

Although it might be tempting to consider Optional for not mandatory method parameters, such a solution pale in comparison with other possible alternatives. To illustrate the problem, examine the following constructor declaration:

public SystemMessage(String title, String content, Optional<Attachment> attachment) {
    // assigning field values
}

At first glance it may look as a right design decision. After all, we explicitly marked the attachment parameter as optional. However, as for calling the constructor, client code can become a little bit clumsy.

SystemMessage withoutAttachment = new SystemMessage("title", "content", Optional.empty());
Attachment attachment = new Attachment();
SystemMessage withAttachment = new SystemMessage("title", "content", Optional.ofNullable(attachment));

Instead of providing clarity, the factory methods of the Optional class only distract the reader. Note there’s only one optional parameter, but imagine having two or three. Uncle Bob definitely wouldn’t be proud of such code

When a method can accept optional parameters, it’s preferable to adopt the well-proven approach and design such case using method overloading. In the example of the SystemMessage class, declaring two separate constructors are superior to using Optional.

public SystemMessage(String title, String content) {
    this(title, content, null);
}

public SystemMessage(String title, String content, Attachment attachment) {
    // assigning field values
}

That change makes client code much simpler and easier to read.

SystemMessage withoutAttachment = new SystemMessage("title", "content");
Attachment attachment = new Attachment();
SystemMessage withAttachment = new SystemMessage("title", "content", attachment);
Matthew Simoneau
  • 6,199
  • 6
  • 35
  • 46
Gili
  • 86,244
  • 97
  • 390
  • 689
  • 19
    That's a lot of copy+paste when only one or two paragraphs are relevant. – Garret Wilson Jan 12 '17 at 16:08
  • 98
    Unfortunately this explanation doesn't address the concern of when the caller is e.g. parsing some information may be optional. With method overloading (as recommended above), the calling code has to say, "Is X present? If so, I'll call overloaded method A. Is Y present? I'll have to call overloaded method B. If X and Y are present, I'll have to call overloaded method C." And so on. There may be a good answer to this, but this explanation of "why" doesn't cover it. – Garret Wilson Jan 12 '17 at 16:10
  • 1
    @GarretWilson You should post a new question instead of commenting on this answer. It will help fleshing out your use-case. – Gili Jan 13 '17 at 17:33
  • I would, if only I had the time. I thought that would be better than nothing. – Garret Wilson Jan 13 '17 at 21:21
  • 9
    Also, when it comes to collections, there is a distinct difference between an empty collection and no collection. For example, a cache. Was it a cache miss? empty optional / null. Was there a cache hit that happens to be an empty collection? full optional / collection. – Ajax Jan 24 '17 at 20:50
  • 2
    @Ajax I think you're misunderstanding the article. They are quoting a point advocated by the [Effective Java](https://www.amazon.ca/Effective-Java-2nd-Joshua-Bloch/dp/0321356683) book, which says that when a method return type is a Collection (not an arbitrary object as a cache would return) then you should favor returning an empty collection instead of `null` or `Optional`. – Gili Jan 25 '17 at 03:31
  • 6
    Sure, you should _favor_ it, but you don't always have control over some code, and, in a very real sense, you may want to differentiate between "there is a value and it is an empty collection" versus "there is no value defined (yet)". Since "magic null" is also a discouraged practice, it is up to you, the developer, to chose the least bad option. I prefer empty optional to represent a cache miss instead of an empty collection, since the actual cached value may *be* an empty collection. – Ajax Jan 25 '17 at 14:07
  • 2
    @Ajax The article doesn't disagree. You should favor returning empty collections unless you have a good reason not to. In your case, you have a good reason not to. – Gili Jan 25 '17 at 15:56
  • 2
    I just wonder how method overloading would be for two or three optional parameters... Uncle Bob still wouldn’t be proud. – user85421 Mar 23 '17 at 10:38
  • 1
    @CarlosHeuberger I would advocate the use of the builder pattern when you have many optional parameters. – Gili Mar 23 '17 at 12:52
  • How about two optional parameters with same class? Like `String key` and `String value`. What if user only want to provide `value`? – Franklin Yu Nov 07 '17 at 16:05
  • 2
    @FranklinYu Static factory methods or the builder pattern are probably appropriate in your case. – Gili Nov 07 '17 at 16:16
  • I don't see Optionals as a distraction in code, actually provides clarity. That's because it states in the contract of the method that an argument value may or not be passed and the method considers that possibility. As opposed to for example having an argument with plain type (as String), in such case we don't know if method checks for null or just throws an exception(breaking the natural flow of execution). – hdkrus Aug 12 '18 at 13:24
  • 1
    @hdkrus if you use overloading you don't need to check anything in implementing code. You just state explicitly that your method can be called with a parameter or without it. As a bonus you have a best error check the one at compile time. – digital_infinity May 11 '20 at 15:33
  • @digital_infinity if you use overloading then you'll have: 1. to repeat code (when you have the argument or not) for each method overload or 2. to pass a null value from the overloaded method without the argument, which would imply you accept nulls in the method with the argument. The whole point here with Optionals is having a readable contract in for arguments allowing null values. – hdkrus May 17 '20 at 03:44
  • 1
    @hdkrus You seem to be conflating absent values with null. The goal isn't "null". The goal is for methods to assign a default value to absent parameters. In my experience, default values are never null. Even if they are null for some reason, the constructor you'd delegate to would be private. Meaning, the use of "null" is an implementation detail. Users would never get to pass in null themselves. – Gili May 18 '20 at 04:57
  • @Gili The thing is that absence of values is not the same that default values either, right now the only true approach to absence of values is "optional" values which is achieved perfectly with Optional type (similarity in the name is not coincidence). The only way to achieve this without Optional would be through default values and null is the common usage. – hdkrus May 18 '20 at 19:25
  • @hdkrus the optionality (in optional value) means do something if this value is absent. Whether it is: do nothing or evaluate (set to) default value. Both of these could be perfectly modeled by overloading without using of null values in public class/interface API. If there is some place where something could be optionally set, maybe it is a place for derived class that must have this field set ? – digital_infinity May 21 '20 at 15:25
  • @digital_infinity, the optional value here has exactly the same meaning that the English word "Optional" and the same meaning that the Option Monad in Functional Programming, and that's: 1. It might have a value, or 2. It might not. It's up to you how you use this semantic in your code, you can asume it to have a default value or you can simply ignore it. – hdkrus May 21 '20 at 23:17
  • 2
    But what if there are 2 or 3 or 4 optional params, that means you overload the method multiple times. It could be 2 ^ #ofParams variations. – Jose Martinez Oct 03 '20 at 14:09
  • @JoseMartinez Static factory methods or the builder pattern are probably more appropriate in your case. – Gili Oct 04 '20 at 17:56
  • @Gil yeah but then the called method will have to do null checks on all the fields. Its easier to use Optional in the implementation of the method being called, versus null checks. Optionals allow for a more fluid/functional/clean way of implementing the method. – Jose Martinez Oct 05 '20 at 11:55
136

There are almost no good reasons for not using Optional as parameters. The arguments against this rely on arguments from authority (see Brian Goetz - his argument is we can't enforce non null optionals) or that the Optional arguments may be null (essentially the same argument). Of course, any reference in Java can be null, we need to encourage rules being enforced by the compiler, not programmers memory (which is problematic and does not scale).

Functional programming languages encourage Optional parameters. One of the best ways of using this is to have multiple optional parameters and using liftM2 to use a function assuming the parameters are not empty and returning an optional (see http://www.functionaljava.org/javadoc/4.4/functionaljava/fj/data/Option.html#liftM2-fj.F-). Java 8 has unfortunately implemented a very limited library supporting optional.

As Java programmers we should only be using null to interact with legacy libraries.

Lii
  • 11,553
  • 8
  • 64
  • 88
Mark Perry
  • 1,656
  • 1
  • 9
  • 6
  • 4
    How about using `@Nonnull` and `@Nullable` from javax.annotation instead? I use them extensively in a library I develop, and the support provided by the IDE (IntelliJ) is very good. – Rogério Jun 10 '16 at 17:06
  • 1
    That is fine, but you have to use this annotation everywhere and you need a library to support methods such as map, flatMap/bind, liftM2, sequence, etc. – Mark Perry Jun 13 '16 at 00:32
  • 23
    *Functional programming languages encourage Optional parameters.* Citation needed. Most functions should not take an optional; instead, the onus of dealing (using appropriate higher-order functions) with the presence or absence of a value is on the caller of the function in question. – jub0bs Jul 28 '16 at 22:06
  • 5
    This. Indeed, none of the answers are convincing enough, and none of them answers this specific question "why using optionals as method parameters is considered bad practice while annotating method parameters with `@NonNull` is totally fine if they serve the same purpose in different ways?" For me, there is only one argument that makes at least some sense: "Optional should be used to provide better APIs which has a clear return type. However for method arguments, overloaded functions can be used." - still, I do not think this is an argument that is strong enough. – Utku Özdemir Sep 13 '16 at 23:15
  • @UtkuÖzdemir Function overloading requires 2^n definition of the same function when I have n optional parameters of different types; in my case n = 3 so I need 8. Or at lease I should have some magic annotation to generate the overloaded functions for me. – Franklin Yu Aug 10 '17 at 17:35
  • One downside of overusing optionals for arguments is having a lot of methods that, together with a totally unnecessary final for every parameter, use it all over the place, you'd get signatures reading "final Optional..., final Optional..., final Optional", the ... signifying the part people tend to skip over because > 10 characters ^^ – yeoman Aug 19 '17 at 10:52
  • 3
    Of course, preferrable to null, but what's even more preferrable is not to need a lot of optional values in the first place because good design :D – yeoman Aug 19 '17 at 10:53
  • 4
    Those comments from Brian Goetz aren't arguments from authority. You provided the reasons for his claims... – Planky Jun 18 '18 at 18:47
  • 2
    I've found that Optionals in parameters makes the code harder to use, and actually adds new ways to introduce bugs. Anything that increases the complexity of use will make bugs more likely. The idea that we should avoid using null has led to code like this: if(Optional.ofNullable(x).isPresent())... instead if(x != null)... All this does is add verbosity and make mistakes more likely. (For example, the coder could accidentally write if(Optional.of(x).isPresent())... which won't work.) Optional are necessary and useful for functional programming. Elsewhere, they're pointless. – MiguelMunoz Oct 05 '18 at 14:25
  • 1
    @MiguelMunoz Optional.ofNullable(x).isPresent() is an ignorance of the standard library. As of Java 8 there's Objects.isNull(x) and Objects.nonNull(x) – pokkanome Mar 26 '19 at 22:04
  • @pokkanome True. I think there was a great enthusiasm for avoiding null errors that people began looking for ways to use Optional to rid their code of nulls, rather than looking for other new methods that are better suited to the task. – MiguelMunoz Mar 29 '19 at 22:39
  • 2
    isNull and nonNull were added for functional programming also, meant to be used as lambdas. But I think there are cases where they make sense outside of functional programming. Bending over backwards to avoid the null keyword is dumb, but limiting its use in simple ways is smart. – pokkanome Mar 30 '19 at 23:30
  • +100 for _As Java programmers we should only be using null to interact with legacy libraries_ – beluchin Jan 20 '21 at 20:28
  • Oh the irony of dismissing an *actual, relevant authority* by [falsely](https://en.wikipedia.org/wiki/Argument_from_authority#:~:text=Some%20consider,given%20context) appealing to the “argument by authority” fallacy, while making claims without providing any evidence or arguments. – Konrad Rudolph Jan 30 '23 at 11:11
50

Let's make something perfectly clear: in other languages, there is no general recommendation against the use of a Maybe type as a field type, a constructor parameter type, a method parameter type, or a function parameter type.

So if you "shouldn't" use Optional as a parameter type in Java, the reason is specific to Optional, to Java, or to both.

Reasoning that might apply to other Maybe types, or other languages, is probably not valid here.

Per Brian Goetz,

[W]e did have a clear intention when adding [Optional], and it was not to be a general purpose Maybe type, as much as many people would have liked us to do so. Our intention was to provide a limited mechanism for library method return types where there needed to be a clear way to represent "no result", and using null for such was overwhelmingly likely to cause errors.

For example, you probably should never use it for something that returns an array of results, or a list of results; instead return an empty array or list. You should almost never use it as a field of something or a method parameter.

So the answer is specific to Optional: it isn't "a general purpose Maybe type"; as such, it is limited, and it may be limited in ways that limit its usefulness as a field type or a parameter type.

That said, in practice, I've rarely found using Optional as a field type or a parameter type to be an issue. If Optional, despite its limitations, works as a parameter type or a field type for your use case, use it.

drew
  • 2,949
  • 3
  • 25
  • 27
12

The pattern with Optional is for one to avoid returning null. It's still perfectly possible to pass in null to a method.

While these aren't really official yet, you can use JSR-308 style annotations to indicate whether or not you accept null values into the function. Note that you'd have to have the right tooling to actually identify it, and it'd provide more of a static check than an enforceable runtime policy, but it would help.

public int calculateSomething(@NotNull final String p1, @NotNull final String p2) {}
Community
  • 1
  • 1
Makoto
  • 104,088
  • 27
  • 192
  • 230
  • 2
    Problem here is rather that @NotNull is not the default case and has to be annotated explicitly - hence boilerplate and stuff that people will just not do due to laziness. – dwegener Jul 30 '16 at 22:39
  • 1
    @dwegener Yes, but `Optional`doesn't fix the issue either, hence my suggestion for the annotations. – Makoto Jul 30 '16 at 22:50
  • 4
    It's a lot harder to ignore Optional than it is to ignore an annotation that you will only notice if reading the docs / source or if your tooling can catch it (static analysis can't always determine nullability correctly). – Ajax Jan 24 '17 at 20:52
  • Note that @Nullable may not mean you "accept" null as a valid value, i.e. not throw any exception. It may just mean that you guard against this case, but will still throw an exception (This is the Findbugs semantics). So you can introduce ambiguity with such annotations instead of clarity. – tkruse Jun 12 '17 at 01:24
  • I'm not sure what ambiguity there is @user2986984. "Not handling null" could only realistically mean an exception is thrown. – Makoto Jun 12 '17 at 01:35
  • The ambiguity is for Nullable, not for Nonnull – tkruse Jun 13 '17 at 10:50
12

This advice is a variant of the "be as unspecific as possible regarding inputs and as specific as possible regarding outputs" rule of thumb.

Usually if you have a method that takes a plain non-null value, you can map it over the Optional, so the plain version is strictly more unspecific regarding inputs. However there are a bunch of possible reasons why you would want to require an Optional argument nonetheless:

  • you want your function to be used in conjunction with another API that returns an Optional
  • Your function should return something other than an empty Optional if the given value is empty
  • You think Optional is so awesome that whoever uses your API should be required to learn about it ;-)
llogiq
  • 13,815
  • 8
  • 40
  • 72
12

Check out the JavaDoc in JDK10, https://docs.oracle.com/javase/10/docs/api/java/util/Optional.html, an API note is added:

API Note: Optional is primarily intended for use as a method return type where there is a clear need to represent "no result," and where using null is likely to cause errors.

Sled
  • 18,541
  • 27
  • 119
  • 168
Xiaolong
  • 121
  • 1
  • 2
12

Maybe I will provoke a bunch of down-votes and negative comments, but... I cannot stand.

Disclaimer: what I write below is not really an answer to the original question, but rather my thoughts on the topic. And the only source for it is my thoughts and my experience (with Java and other languages).

First let's check, why would anyone like to use Optional at all?

For me the reason is simple: unlike other languages java does not have built-in capability to define variable (or type) as nullable or not. All "object"-variables are nullable and all primitive-types are not. For the sake of simplicity let't not consider primitive types in further discussion, so I will claim simply that all variables are nullable.

Why would one need to declare variables as nullable/non-nullable? Well, the reason for me is: explicit is always better, than implicit. Besides having explicit decoration (e.g. annotation or type) could help static analyzer (or compiler) to catch some null-pointer related issues.

Many people argue in the comments above, that functions do not need to have nullable arguments. Instead overloads should be used. But such statement is only good in a school-book. In real life there are different situations. Consider class, which represents settings of some system, or personal data of some user, or in fact any composite data-structure, which contains lots of fields - many of those with repeated types, and some of the fields are mandatory while others are optional. In such cases inheritance/constructor overloads do not really help.

Random example: Let's say, we need to collect data about people. But some people don't want to provide all the data. And of course this is POD, so basically type with value-semantics, so I want it to be more or less immutable (no setters).

class PersonalData {
    private final String name; // mandatory
    private final int age; // mandatory
    private final Address homeAddress; // optional
    private final PhoneNumber phoneNumber; // optional. Dedicated class to handle constraints
    private final BigDecimal income; // optional.
    // ... further fields

    // How many constructor- (or factory-) overloads do we need to handle all cases
    // without nullable arguments? If I am not mistaken, 8. And what if we have more optional
    // fields?

    // ...
}

So, IMO discussion above shows, that even though mostly we can survive without nullable arguments, but sometimes it is not really feasible.

Now we come to the problem: if some of the arguments are nullable and others are not, how do we know, which one?

Approach 1: All arguments are nullable (according to java standrd, except primitive types). So we check all of them.

Result: code explodes with checks, which are mostly unneeded, because as we discussed above almost all of the time we can go ahead with nullable variables, and only in some rare cases "nullables" are needed.

Approach 2: Use documentation and/or comments to describe, which arguments/fields are nullable and which not.

Result: It does not really work. People are lazy to write and read the docs. Besides lately the trend is, that we should avoid writing documentation in favor of making the code itself self-describing. Besides all the reasoning about modifying the code and forgeting to modify the documentation is still valid.

Approach 3: @Nullable @NonNull etc... I personally find them to be nice. But there are certain disadvantages : (e.g. they are only respected by external tools, not the compiler), the worst of which is that they are not standard, which means, that 1. I would need to add external dependency to my project to benefit from them, and 2. The way they are treated by different systems are not uniform. As far as I know, they were voted out of official Java standard (and I don't know if there are any plans to try again).

Approach 4: Optional<>. The disadvantages are already mentioned in other comments, the worst of which is (IMO) performance penalty. Also it adds a bit of boilerplate, even thoough I personally find, use of Optional.empty() and Optional.of() to be not so bad. The advantages are obvious:

  1. It is part of the Java standard.
  2. It makes obvious to the reader of the code (or to the user of API), that these arguments may be null. Moreover, it forces both: user of the API and developer of the method to aknolage this fact by explicitly wrapping/unwrapping the values (which is not the case, when annotations like @Nullable etc. are used).

So in my point, there is no black-and-white in regard of any methodology including this one. I personally ended up with the following guidelines and conventions (which are still not strict rules):

  1. Inside my own code all the variables must be not-null (but probably Optional<>).
  2. If I have a method with one or two optional arguments I try to redesign it using overloads, inheritance etc.
  3. If I cannot find the solution in reasonable time, I start thinking, if the performance is critical (i.e. if there are millions of the objects to be processed). Usually it is not the case.
  4. If not, I use Optional as argument types and/or field types.

There are still grey areas, where these conventions do not work:

  • We need high performance (e.g. processing of huge amounts of data, so that total execution time is very large, or situations when throughput is critical). In this cases performance penalty introduced by Optional may be really unwanted.
  • We are on the boundary of the code, which we write ourselves, e.g.: we read from the DB, Rest Endpoint, parse file etc.
  • Or we just use some external libraries, which do not follow our conventions, so again, we should be careful...

By the way, the last two cases can also be the source of need in the optional fields/arguments. I.e. when the structure of the data is not developed by ourselves, but is imposed by some external interfaces, db-schemas etc...

At the end, I think, that one should think about the problem, which is being solved, and try to find the appropriate tools. If Optional<> is appropriate, then I see no reason not to use it.

Edit: Approach 5: I used this one recently, when I could not use Optional. The idea is simply to use naming convention for method arguments and class variables. I used "maybe"-prefix, so that if e.g. "url" argument is nullable, then it becomes maybeUrl. The advantage is that it slightly improves understandability of the intent (and does not have disadvantages of other approaches, like external dependencies or performance penalty). But there are also drawbacks, like: there is no tooling to support this convention (your IDE will not show you any warning, if you access "maybe"-variable without first checking it). Another problem is that it only helps, when applied consistently by all people working on the project.

Dmitrii Semikin
  • 2,134
  • 2
  • 20
  • 25
  • Regarding the last paragraph, my convention is a post-fix `OrNull` for example `getUrlOrNull()` which is clear to the user that this method might potentially return a null. – Jasper Citi Oct 29 '21 at 04:12
  • Finally someone said it, use of Optional introduces a performance penalty. – djtubig-malicex Oct 19 '22 at 07:00
9

This seems a bit silly to me, but the only reason I can think of is that object arguments in method parameters already are optional in a way - they can be null. Therefore forcing someone to take an existing object and wrap it in an optional is sort of pointless.

That being said, chaining methods together that take/return optionals is a reasonable thing to do, e.g. Maybe monad.

Steve B.
  • 55,454
  • 12
  • 93
  • 132
9

Accepting Optional as parameters causes unnecessary wrapping at caller level.

For example in the case of:

public int calculateSomething(Optional<String> p1, Optional<BigDecimal> p2 {}

Suppose you have two not-null strings (ie. returned from some other method):

String p1 = "p1"; 
String p2 = "p2";

You're forced to wrap them in Optional even if you know they are not Empty.

This get even worse when you have to compose with other "mappable" structures, ie. Eithers:

Either<Error, String> value = compute().right().map((s) -> calculateSomething(
< here you have to wrap the parameter in a Optional even if you know it's a 
  string >));

ref:

methods shouldn't expect Option as parameters, this is almost always a code smell that indicated a leakage of control flow from the caller to the callee, it should be responsibility of the caller to check the content of an Option

ref. https://github.com/teamdigitale/digital-citizenship-functions/pull/148#discussion_r170862749

gpilotino
  • 13,055
  • 9
  • 48
  • 61
5

My take is that Optional should be a Monad and these are not conceivable in Java.

In functional programming you deal with pure and higher order functions that take and compose their arguments only based on their "business domain type". Composing functions that feed on, or whose computation should be reported to, the real-world (so called side effects) requires the application of functions that take care of automatically unpacking the values out of the monads representing the outside world (State, Configuration, Futures, Maybe, Either, Writer, etc...); this is called lifting. You can think of it as a kind of separation of concerns.

Mixing these two levels of abstraction doesn't facilitate legibility so you're better off just avoiding it.

Eddy
  • 1,662
  • 2
  • 21
  • 36
4

So, if you would permit the pun, Oracle issued an oracle:

Thou shalt not use Optional but for function return values.

I love it how most of the answers so far are going along with the narrative of Oracle's oracle, which is re-iterated unquestioned all over the interwebz, in the "many Web sites" mentioned in the question. This is very typical of stack overflow: if something is allegedly supposed to be a certain way, and you ask why it is supposed to be that way, almost everyone will offer reasons why; almost nobody will question whether it should in fact be that way.

So, here is a dissenting opinion:

You can completely eliminate null from your code base by using Optional everywhere: not only in function return values, but also in function parameters, in class members, in array members, even in local variables.

I have done it in a 100k-lines-of-code project. It worked.

If you decide to go along this path, then you will need to be thorough, so you will have a lot of work to do. The example mentioned in the accepted answer with Optional.ofNulable() should never occur, because if you are thorough, then you should not have null anywhere, and therefore no need for Optional.ofNullable(). In that 100k-lines-of-code project that I mentioned above, I have only used Optional.ofNullable() a couple of times when receiving results from external methods that I have no control over.

Now, if you decide to go along this path, your solution will not be the most performant solution possible, because you will be allocating lots of instances of Optional. However:

  1. It is nothing but a runtime performance disadvantage.
  2. Although the disadvantage is non-negligible, it is also not severe.
  3. It is Java's problem, not your problem.

Let me explain that last bit.

Java does not offer explicit nullability of reference types as C# does (since version 8.0) so it is inferior in this regard. (I said "in this regard"; in other regards, Java is better; but that's off-topic right now.)

The only proper alternative to explicit nullability of reference types is the Optional type. (And it is arguably even slightly better, because with Optional you can indicate optional-of-optional if you must, whereas with explicit nullability you cannot have ReferenceType??, or at least you cannot in C# as it is currently implemented.)

Optional does not have to add overhead, it only does so in Java. That's because Java also does not support true value types, as C# and Scala do. In this regard, Java is severely inferior to those languages. (Again, I said "in this regard"; in other regards, Java is better; but that's off-topic right now.) If Java did support true value types, then Optional would have been implemented as a single machine word, which would mean that the runtime overhead of using it would be zero.

So, the question that it ultimately boils down to is:

Do you want perfect clarity and type safety in your code, or do you prefer maximum performance?

I believe that for high-level languages, (of which Java certainly aims to be one,) this question was settled a long time ago.

Mike Nakis
  • 56,297
  • 11
  • 110
  • 142
2

Another reason to be carefully when pass an Optional as parameter is that a method should do one thing... If you pass an Optional param you could favor do more than one thing, it could be similar to pass a boolean param.

public void method(Optional<MyClass> param) {
     if(param.isPresent()) {
         //do something
     } else {
         //do some other
     }
 }
Pau
  • 14,917
  • 14
  • 67
  • 94
  • 2
    I think that's not a good reason. The same logic could be applied for checking if param is null when not optional is used. Actually, the `if(param.isPresent())` is not the best approach to use Optional, instead you may use: `param.forEach(() -> {...})` – hdkrus Aug 12 '18 at 13:29
  • I don’t like the idea of passing nullable params neither. Anyway, I said you could favor, of course there are exceptions you may use it, it’s up to you, just use it carefully, that’s all. There isn’t any rule which applies for all the scenarios. – Pau Aug 12 '18 at 14:27
2

Using Optional as parameters might be useful in some use cases which involves protobufs or setting fields in a configuration object.

public void setParameters(Optional<A> op1, Optional<B> op2) {
    ProtoRequest.Builder builder = ProtoRequest.newBuilder();
    op1.ifPresent(builder::setOp1);
    op2.ifPresent(builder::setOp2);
...
}

I think in such cases it might be useful to have optional as parameters. API receiving the proto request would handle the different fields. If a function is not doing additional computations on these parameters then using Optional might be simpler.

public void setParameters(A op1, B op2) {
    ProtoRequest.Builder builder = ProtoRequest.newBuilder();
    if (op1 != null) {
        builder.setOp1(op1);
    }
    if (op2 != null) {
        builder.setOp2(op2);
    }
...
}
low_key
  • 21
  • 1
1

I think that is because you usually write your functions to manipulate data, and then lift it to Optional using map and similar functions. This adds the default Optional behavior to it. Of course, there might be cases, when it is necessary to write your own auxilary function that works on Optional.

Danil Gaponov
  • 1,413
  • 13
  • 23
1

I believe the reson of being is you have to first check whether or not Optional is null itself and then try to evaluate value it wraps. Too many unnecessary validations.

Macchiatow
  • 607
  • 10
  • 24
  • 5
    Passing a null Optional reference can be considered a programming error (because Optional provides an *explicit* way of passing an "empty" value), so it's not worth checking, unless you want to avoid throwing exceptions in all cases. – pvgoran May 13 '16 at 07:22
1

I know that this question is more about opinion rather than hard facts. But I recently moved from being a .net developer to a java one, so I have only recently joined the Optional party. Also, I'd prefer to state this as a comment, but since my point level does not allow me to comment, I am forced to put this as an answer instead.

What I have been doing, which has served me well as a rule of thumb. Is to use Optionals for return types, and only use Optionals as parameters, if I require both the value of the Optional, and weather or not the Optional had a value within the method.

If I only care about the value, I check isPresent before calling the method, if I have some kind of logging or different logic within the method that depends on if the value exists, then I will happily pass in the Optional.

Blair
  • 61
  • 1
  • 5
0

Optionals aren't designed for this purpose, as explained nicely by Brian Goetz.

You can always use @Nullable to denote that a method argument can be null. Using an optional does not really enable you to write your method logic more neatly.

Community
  • 1
  • 1
Kieran
  • 343
  • 2
  • 10
  • 10
    I'm sorry, but I cannot agree with the argument "wasn't designed" or "somebody recommends against it". An an engineer we should be specific. Using @Nullable is much worse tham using Optional, because Optionals are much more verbose from an API point of view. I don't see any good reasons against using Optionals as arguments (provided you don't want to use method overloading) – Kacper86 Nov 09 '17 at 05:53
  • 1
    Am I wrong, or @Nullable is not in part of java standard? As far as I remember it was voted out... – Dmitrii Semikin Nov 10 '20 at 15:10
  • There is indeed no `@Nullable` in the JRE. It is however specified in [JSR 305](https://dzone.com/articles/when-to-use-jsr-305-for-nullability-in-java), and multiple libraries have their own [version](https://stackoverflow.com/questions/19030954/cant-find-nullable-inside-javax-annotation). It was also part of `jaxb` and intended for XML marshalling only (before that was removed from the JRE to a separate artifact). But those were in the `com.sun.istack.internal` package, so a double no-no (you're not supposed to depend on legacy sun packages nor on internal implementations). – slindenau Feb 12 '23 at 15:17
0

One more approach, what you can do is

// get your optionals first
Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();

// bind values to a function
Supplier<Integer> calculatedValueSupplier = () -> { // your logic here using both optional as state}

Once you have built a function(supplier in this case) you will be able to pass this around as any other variable and would be able to call it using

calculatedValueSupplier.apply();

The idea here being whether you have got optional value or not will be internal detail of your function and will not be in parameter. Thinking functions when thinking about optional as parameter is actually very useful technique that I have found.

As to your question whether you should actually do it or not is based on your preference, but as others said it makes your API ugly to say the least.

Swaraj Yadav
  • 83
  • 1
  • 6
0

At first, I also preferred to pass Optionals as parameter, but if you switch from an API-Designer perspective to a API-User perspective, you see the disadvantages.

For your example, where each parameter is optional, I would suggest to change the calculation method into an own class like follows:

Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();

MyCalculator mc = new MyCalculator();
p1.map(mc::setP1);
p2.map(mc::setP2);
int result = mc.calculate();
Torsten
  • 96
  • 1
  • 6
  • 1
    A good API designer always looks from the user perspective. Perhaps it would be better to say this is API implementor vs API user :) – john16384 Nov 04 '20 at 13:39
0

This is because we have different requirements to an API user and an API developer.

A developer is responsible for providing a precise specification and a correct implementation. Therefore if the developer is already aware that an argument is optional the implementation must deal with it correctly, whether it being a null or an Optional. The API should be as simple as possible to the user, and null is the simplest.

On the other hand, the result is passed from the API developer to the user. However the specification is complete and verbose, there is still a chance that the user is either unaware of it or just lazy to deal with it. In this case, the Optional result forces the user to write some extra code to deal with a possible empty result.

speedogoo
  • 2,828
  • 2
  • 17
  • 19
0

First of all, if you're using method 3, you can replace those last 14 lines of code with this:

int result = myObject.calculateSomething(p1.orElse(null), p2.orElse(null));

The four variations you wrote are convenience methods. You should only use them when they're more convenient. That's also the best approach. That way, the API is very clear which members are necessary and which aren't. If you don't want to write four methods, you can clarify things by how you name your parameters:

public int calculateSomething(String p1OrNull, BigDecimal p2OrNull)

This way, it's clear that null values are allowed.

Your use of p1.orElse(null) illustrates how verbose our code gets when using Optional, which is part of why I avoid it. Optional was written for functional programming. Streams need it. Your methods should probably never return Optional unless it's necessary to use them in functional programming. There are methods, like Optional.flatMap() method, that requires a reference to a function that returns Optional. Here's its signature:

public <U> Optional<U> flatMap(Function<? super T, ? extends Optional<? extends U>> mapper)

So that's usually the only good reason for writing a method that returns Optional. But even there, it can be avoided. You can pass a getter that doesn't return Optional to a method like flatMap(), by wrapping it in a another method that converts the function to the right type. The wrapper method looks like this:

public static <T, U> Function<? super T, Optional<U>> optFun(Function<T, U> function) {
    return t -> Optional.ofNullable(function.apply(t));
}

So suppose you have a getter like this: String getName()

You can't pass it to flatMap like this:

opt.flatMap(Widget::getName) // Won't work!

But you can pass it like this:

opt.flatMap(optFun(Widget::getName)) // Works great!

Outside of functional programming, Optionals should be avoided.

Brian Goetz said it best when he said this:

The reason Optional was added to Java is because this:

return Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
    .stream()
    .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
    .filter(m ->  Arrays.equals(m.getParameterTypes(), parameterClasses))
    .filter(m -> Objects.equals(m.getReturnType(), returnType))
    .findFirst()
    .getOrThrow(() -> new InternalError(...));

is cleaner than this:

Method matching =
    Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
    .stream()
    .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
    .filter(m ->  Arrays.equals(m.getParameterTypes(), parameterClasses))
    .filter(m -> Objects.equals(m.getReturnType(), returnType))
    .getFirst();
if (matching == null)
  throw new InternalError("Enclosing method not found");
return matching;
MiguelMunoz
  • 4,548
  • 3
  • 34
  • 51
  • 2
    Well, that's ONE of the reasons it was added to Java. There are plenty of others. Replacing long chains of nested 'if' statements which traverse into a data structure with a single sequence of chained calls to Optional.map is my personal favorite. The Functional Languages programming world has many interesting uses for Optional besides simply replacing null checks like this. – Some Guy Dec 10 '18 at 08:00
0

Irrespective of Java 8, Use old school method overloading technique to bring clarity and flexibility, suppose you have following method with two args

public void doSomething(arg1,arg2);

in case you want to add additional optional parameter then overload the method

public void doSomething(arg1,arg2,arg3) {
Result result = doSomething(arg1,arg2);
// do additional working
}
Dharman
  • 30,962
  • 25
  • 85
  • 135
Ali
  • 43
  • 1
  • 18
0

A good example were Optional as arguments would be nice is JPA Repositories. Id love to do something like findByNameAndSurname(Optional,Optional). That way, if the Optional is empty, no WHERE param=y is performed

Whimusical
  • 6,401
  • 11
  • 62
  • 105
0

If wanting an optional primitive type, just use the primitive's class.

For those coming from a C++ background and got confused.

E.g. for an optional boolean, use Boolean.

The biggest problem is that Optional<Boolean> itself could be set to null, so now you have three states instead of two.

E.g. Optional<Boolean> can be (null, empty, or not-empty) but Boolean can only be (null or non-empty).

Chris Happy
  • 7,088
  • 2
  • 22
  • 49
0

I noticed that you mentioned a a number of approaches, and during the years since the question was asked, one alternative wasn't really explored - the Builder design pattern.

The idea is that with a builder you only set those parameters that you need, and it scales very well as the number of arguments grows (as opposed to overloading).

Here's an example of a class you could use:

public class Something {
    private String p1;
    private Integer p2;

    private Something(String p1, Integer p2) {
        this.p1 = p1;
        this.p2 = p2;
    }

    public static class Builder {

        private String p1;
        private Integer p2;

        public Builder setP1(String value) {
            this.p1 = value;
            return this;
        }

        public Builder setP2(Integer value) {
            this.p2 = value;
            return this;
        }

        public Something build() {
            // Validate input and set defaults here

            return new Something(p1, p2);
        }
    }
}

Here's how you'd use it:

var something = Something.Builder().setP1("blah").setP2(12345).build();
calculate(something); // or something.calculate()

Now you may notice that the above implementation contains a fair amount of boilerplate code, which is its main downside. Having said that, there are libraries that eliminate it for you, e.g. FreeBuilder, making the code short and tidy (relatively speaking - Java isn't known for its code compactness).

Jean Spector
  • 916
  • 9
  • 9