7

Suppose I have a library method like this (very abbreviated):

public static <V> Optional<V> doSomethingWith(Callable<V> callable) {
    try {
        return Optional.of(callable.call());
    } catch (Exception ex) {
        // Do something with ex
        return Optional.empty();
    }
}

And I want to something that doesn't return a value, like:

    Library.</*What1*/>doSomethingWith(() -> {
        foo();
        return /*what2*/;
    });

My first instinct for a generic method that doesn't return a value is making the type Void and returning null, however because the result gets wrapped in an Optional this would throw an exception.

What are reasonable placeholders for /*What1*/ and /*what2*/ that don't look totally random like Integer and 0?

[edit] I'm trying to avoid Optional.ofNullable because empty is used here to indicate that callable.call() did not complete normally.

Mark Jeronimus
  • 9,278
  • 3
  • 37
  • 50
  • 3
    Can't you change library method to use `Optional.ofNullable`? What's the point here to return optional if it's always present? – Tagir Valeev Dec 28 '16 at 10:56
  • Library method is very abbreviated. It can actually return `Optional.empty()` when a condition occurs. – Mark Jeronimus Dec 28 '16 at 10:57
  • You could just add a reasonably-named local class and return an instance of that class – glee8e Dec 28 '16 at 11:02
  • 1
    `Optional` is [a container object which may or may not contain a non-null value.](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html) So what is the question? – user207421 Dec 28 '16 at 11:03
  • 1
    @MarkJeronimus if you cannot modify the library, seems that you cannot do this legally without ugly hacks like unchecked cast. Can you consider changing the type of your callable to something else (like `Callable` and return `Boolean.TRUE`)? – Tagir Valeev Dec 28 '16 at 11:13
  • 2
    Possible duplicate of [Java generics void/Void types](http://stackoverflow.com/questions/5568409/java-generics-void-void-types) – Joe Dec 28 '16 at 12:09
  • 2
    You can also create a custom enum (like `Result` or something) with exactly one constant (like `EMPTY` or something). – MC Emperor Dec 28 '16 at 16:33
  • @Joe It's not a duplicate, please revoke. That other question asks how to return nothing, and the accepted answer is `Void`. I'm asking what I can use instead of `Void` to return something besides `null` as a 'nothing' marker. – Mark Jeronimus Dec 29 '16 at 09:39
  • 1
    It seems to me that what you need is the type that some other languages call "Unit": a type with exactly one value (whereas Void has no possible values). The suggestion of an enum with only one value is a good one. – Daniel Martin Feb 05 '17 at 19:17

4 Answers4

6

If you need a type hint for a generic parameter that will never be used you can use Void, the JDK does this too in some cases, e.g. when converting Runnable into CompletableFuture<T> it uses Void for T.

If you use Optional.ofNullable then you can just return null for what2, which is the only valid value for Void.

[edit] I'm trying to avoid Optional.ofNullable because empty is used here to indicate that callable.call() did not complete normally.

Then you're using the wrong tool for the job. CompletionStage or CompletableFuture has the right semantics.

the8472
  • 40,999
  • 5
  • 70
  • 122
4

I usually use Boolean.TRUE to mark success, but you could return Void.class as well. Both are cheap in the sense that not every return creates a new object to be discarded. Though Class<Void> is not just Void it may serve the purpose of labelling something as void just as well.

As already mentioned you could also create your own Result-class/-enum.

Or you could of course return Optional.<Void>nothing(), too. This would result in some Optional<Optional<Void>>, but also do the trick.

If you think all of the above is ugly, I fear that the API probably isn't to well tailored to your needs. Raise an issue/pull request or look for something else.

TheConstructor
  • 4,285
  • 1
  • 31
  • 52
  • I like the enum option best. Especially because yesterday I was just wondering about why there is no built-in (non-primitive) type that has exactly 2 possibilities. To compare: `Void` has one possibility: `null`, and `Boolean` has three: `null`, `FALSE`, `TRUE`. (`Object` of course has infinite possibilities.) – Mark Jeronimus Dec 29 '16 at 09:36
1

You could also create your own type similar to Void

public class Result {
     public static final Result OK = new Result();

     private Result(){}
}

and then return Result.OK.

You can also enhance this type to represent also errors, if you need.

But maybe using java Void is preferable if you don't need anything special.

rascio
  • 8,968
  • 19
  • 68
  • 108
  • I would use an enum for this. – TheConstructor Dec 29 '16 at 08:49
  • It depends on what you have to do, with the singleton you can't manage the ERROR, because with it you will need a different instance each time with a different message. – rascio Dec 29 '16 at 10:35
  • Enums inherit some more methods (like `toString`), why I usually prefer them for signalling-instances like the proposed `Result.OK`. OP's problem would not exist if some `ResultOrError`-class would have been used in the API instead of `Optional`. – TheConstructor Dec 29 '16 at 10:41
0

Use Void for the return type, which is the logical choice for "nothing", but actually return an instance of Void.

Although the javadoc for Void says it's:

...an uninstantiable placeholder class...

You can nevertheless instantiate it:

try {
    Constructor<Void> c = Void.class.getDeclaredConstructor();
    c.setAccessible(true);
    return c.newInstance();
} catch (Exception perfunctory) {
    return null; // won't happen
}
Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • 8
    Bad advice. You break several contracts when you hack an instance of `Void` into existence, not to mention possible implementation changes in future/alternate JDKs that cause the `won't happen` to happen anyway. – Mark Jeronimus Dec 28 '16 at 13:49
  • @mark which contract(s), **specifically**, are broken? Please provide links etc. I maintain that there are no such contracts and that if future versions change the constructor such that this code won't work (very unlikely), worry about that then. – Bohemian Dec 28 '16 at 17:05