5

1) In Java, I can do this:

Void z = null;

Is there any other value except null I can assign to z?

2) Consider the following code snipped:

Callable<Void> v = () -> {
    System.out.println("zzz"); 
    Thread.sleep(1000);
    return null;
}; 

This compiles OK, but if I remove the last statement return null; it doesn't. Why? After all, Void is supposed to mean no return value.

Andriy Ivaneyko
  • 20,639
  • 6
  • 60
  • 82
peter.petrov
  • 38,363
  • 16
  • 94
  • 159
  • 1
    void and Void are two different things, the latter one is a class – Mateusz Dymczyk Jan 14 '16 at 13:47
  • I know they are different things but they are still related: "The Void class is an un-instantiable placeholder class to hold a reference to the Class object representing the Java keyword void". – peter.petrov Jan 14 '16 at 13:48
  • Possible duplicate of [Uses for the Java Void Reference Type?](http://stackoverflow.com/questions/643906/uses-for-the-java-void-reference-type) – Andremoniy Jan 14 '16 at 13:53
  • yes but still you are defining a callable returning an instance of Void, void is not an instance of Void hence the compilation error – Mateusz Dymczyk Jan 14 '16 at 13:54
  • Point 2) logically follows from 1): if you can assign single value (`null` value), it is really single value which you can (and should) return from appropriate lambda. Nothing strange – Andremoniy Jan 14 '16 at 13:55
  • @Andremoniy Well, I can still say `Void z;` without assigning anything to `z`. I don't think 2) follows from 1) just like that. Still, that's an interesting way of viewing this... Actually maybe you're right, it follows. – peter.petrov Jan 14 '16 at 13:57
  • `Void z;` is obviously unitialized variable, which you will have to initialize with some (and only possible - `null`) value before usage. So this is not different case. – Andremoniy Jan 14 '16 at 13:59
  • 1
    http://stackoverflow.com/questions/2408626/returning-a-void-object – Andremoniy Jan 14 '16 at 14:01
  • OK, well, thanks everyone. – peter.petrov Jan 14 '16 at 14:04
  • If you say `Void z;` as a field, then `z` will be initialized with `null`, just like 1). – Erick G. Hagstrom Jan 14 '16 at 15:56
  • 1
    Just as an FYI, `null` IS A value, it's just a special value. Returning null and not returning anything are different things. – JNYRanger Jan 14 '16 at 16:32

5 Answers5

8

From the docs:

The Void class is an uninstantiable placeholder class to hold a reference to the Class object representing the Java keyword void.

So, no.

Void is used by methods having to return an object, but really returning nothing.

A decent example can be observed with some usage of the AsyncTask in Android, in cases where you don't need to return any object after the task is complete.

You would then extend AsyncTask<[your params type], [your progress type], Void>, and return null in your onPostExecute override.

You wouldn't need it in most cases though (for instance, Runnable is typically more suitable than Callable<Void>).

Ansering your question more specifically:

But if I remove the return null it does not compile?! Why?

... because a Void is still an object. However, it can only have value null.

If your method declares it returns Void, you need to (explicitly) return null.

Mena
  • 47,782
  • 11
  • 87
  • 106
  • Thanks. Still, Runnable's run() does not throw any checked exceptions. This lambda expression from my example does. So Runnable will not work in my example. – peter.petrov Jan 14 '16 at 13:49
  • @peter.petrov you're welcome. There are cases when using `Void` is.. ehm... unavoidable (sorry). – Mena Jan 14 '16 at 14:25
  • @peter.petrov although you can still use a `Runnable` in your example and wrap the `sleep` invocation in a `try` / `catch`. – Mena Jan 14 '16 at 14:27
3

If you check the sources:

package java.lang;

public final class Void {
    public static final Class<Void> TYPE = Class.getPrimitiveClass("void");

    private Void() {
    }
}

Void is:

  • final class;
  • has private constructor.

Without using Reflection it's not possible to assign anything but null to a reference of Void type.

Denis Kulagin
  • 8,472
  • 17
  • 60
  • 129
2

In Java, I can do this Void z = null; Is there any other value (but null) which I can assign to z ?

You can if you create you own Void instances. You can use Reflection or Unsafe to create these, not that it's a good idea.

But if I remove the return null it does not compile?! Why? After all, Void is supposed to mean just that - no return type.

Java is case sensitive, this means that Boolean and boolean are NOT the same type nor is Void and void. Void is a notional wrapper for void but otherwise is just a class you shouldn't create any instance of.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
1

Maybe what you are asking for is Runnable or Consumer - some interface that doesn't have a return value. Void only serves to show that you cannot expect anything else than null. It is still just a class, not a keyword or anything special. A class that cannot be instantiated, so you have to return null.

Vlasec
  • 5,500
  • 3
  • 27
  • 30
1

A lot of efforts were spent in designing lambda expression to treat int/Integer etc indistinguishably, so that int->Long will be compatible with Integer->long, etc.

It is possible (and desirable) to treat void/Void in a similar way, see comments from Goetz and Forax. However, they didn't have the time to implement the idea for java8 :(

You can introduce an adapter type that is both ()->void and ()->Void; it can simplify your use case a little bit, see http://bayou.io/release/0.9/javadoc/bayou/util/function/Callable_Void.html


If you have a method that accepts ()->Void, it is not going to work well with ()->void lambdas. One workaround is to overload the method to accept ()->void. For example, ExecutorService

    submit(Callable<T> task)
    submit(Runnable task)

    ...
        submit( System::gc );   // ()->void

However, overloading with functional parameter types is tricky... The example above works because both accept a zero-arg function. If the function has non-zero args

    foo( Function<String,Void> f )   //  String->Void
    foo( Consumer<String>      f )   //  String->void

it's confusing to the compiler (and the programmer)

    foo( str->System.out.println(str) );  // which foo?
    foo( System.out::println );           // which foo?

Given an implicit lambda str->expr, the compiler needs a target type to make sense of it. The target type here is given by the method parameter type. If the method is overloaded, we need to resolve method overloading first... which typically depends on the type of the argument (the lambda)... So you can see why it is complicated.

(A zero-arg lambda is never implicit. All argument types are known, since there's no argument.)

The lambda spec does have provisions to resolve the following cases

    foo( str->{ System.out.println(str); } );
    foo( str->{ System.out.println(str); return null; } );

You may argue that in the previous example,

    foo( str->System.out.println(str) );

since println(str) returns void, the Void version obviously does not fit, therefore the compiler should be able to resolve it. However, remember that, to know the meaning of println(str), first, the type of str must be resolved, i.e. method overloading of foo must be resolved first...

Although in this case, str is unambiguously String. Unfortunately, the lambda designer decided against to be able to resolve that, arguing it is too complicated. This is a serious flaw, and it is why we cannot overload methods like in Comparator

    comparing( T->U )

  //comparing( T->int )       // overloading won't work well

    comparingInt ( T->int )   // use a diff method name instead
ZhongYu
  • 19,446
  • 5
  • 33
  • 61