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.

11
  • 1
    void and Void are two different things, the latter one is a class Commented Jan 14, 2016 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". Commented Jan 14, 2016 at 13:48
  • Possible duplicate of Uses for the Java Void Reference Type? Commented Jan 14, 2016 at 13:53
  • 1
    stackoverflow.com/questions/2408626/returning-a-void-object Commented Jan 14, 2016 at 14:01
  • 1
    Just as an FYI, null IS A value, it's just a special value. Returning null and not returning anything are different things. Commented Jan 14, 2016 at 16:32

5 Answers 5

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.

Sign up to request clarification or add additional context in comments.

3 Comments

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 you're welcome. There are cases when using Void is.. ehm... unavoidable (sorry).
@peter.petrov although you can still use a Runnable in your example and wrap the sleep invocation in a try / catch.
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.

Comments

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.

Comments

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.

Comments

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.