Is there any statement in Java which "void" (with small v, returned from a method) can participate in?
For example it seems it not even possible to do voidMe() instanceof Void where:
void voidMe(){...}.
(This is a theoretical question out of curiosity.)
Yes - a simple method call:
voidMe();
That's a statement.
If you mean an expression, then
voidMe()
is still an expression too - just one where the result is nothing. From section 15.1 of the JLS:
When an expression in a program is evaluated (executed), the result denotes one of three things:
A variable (§4.12) (in C, this would be called an lvalue)
A value (§4.2, §4.3)
Nothing (the expression is said to be void)
...
An expression denotes nothing if and only if it is a method invocation (§15.12) that invokes a method that does not return a value, that is, a method declared void (§8.4). Such an expression can be used only as an expression statement (§14.8), because every other context in which an expression can appear requires the expression to denote something.
The highlighted part basically states that the only use of an expression such as voidMe() is a method invocation. There's no "bigger" expression that can have voidMe() as a part of it.
I copied from Java Specification §8.4.2
A method declaration d1 with return type R1 is return-type-substitutable for another method d2 with return type R2, if and only if the following conditions hold:
If R1 is void then R2 is void.
It can be used by Class literals Java Specification §15.8.2
The type of void.class (§8.4.5) is Class<Void>.
...
A class literal evaluates to the Class object for the named type (or for void)
as defined by the defining class loader of the class of the current instance.
In Java, void is not a type or an object. Thus it cannot be part of an "expression".
Any operator or function in an expression would use its arguments as input. However, since void is not a valid type, and there is no object of type void, it would not be possible to evaluate the expression.
Thus, any void function must be a statement by itself.
You also cannot create an instance of Void, because "The Void class is an uninstantiable placeholder class".
Void is uninstantiable and thus you cannot use its value anywhere. (Seriously, what would you want to do with nothing? How can you even have an instance of nothing?)
You can however declare Void as a type parameter. This is necessary if you want to use some classes from the java.concurrent package. For example, you need to declare a Future<Void> in order to submit a void method onto an executor service. In one place you can then ask for result, which will be of type Void. This result is always null.
Related
I was expecting these to be simple drop-in replacements for each other, but they're not. Clearly I'm not understanding the notation.
Can anyone explain why does that happen?
playButton.setOnAction(e -> track.play());
Here, compiler is happy with play() having a signature of
void play()
but here
playButton.setOnAction(track::play);
it requires
void play(Event e)
Here is a quote from the Java language specification:
A method reference expression (§15.13) is potentially compatible with
a functional interface type T if, where the arity of the function type
of T is n, there exists at least one potentially applicable method
when the method reference expression targets the function type with
arity n (§15.13.1), and one of the following is true:
The method reference expression has the form ReferenceType :: [TypeArguments] Identifier and at least one potentially applicable
method is either (i) static and supports arity n, or (ii) not static
and supports arity n-1.
The method reference expression has some other form and at least one potentially applicable method is not static.
...
The definition of potential applicability goes beyond a basic arity
check to also take into account the presence and "shape" of functional
interface target types.
Every method reference should conform to a function interface (which is an interface that declares a single abstract method, non-overriding methods from Object class). The compiler needs to verify whether the provided reference resolves to a single existing method that has required arity (number of parameters) and their types match the types of the method declared by the target functional interface.
Let's have a look at the prior Java 8 code (code-sample from JavaFX tutorial by created Oracle):
button2.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
label.setText("Accepted");
}
});
That is the "shape" that needs to be filled with a code describing an action. Method handle() of the EventHandler interface expects an event as an argument. Whether it would be used or not, that's up to you, the key point is that the abstract method of the target interface expects this argument to be provided.
By using a lambda expression e -> track.play() you're explicitly telling to ignore it.
And when you're passing a method reference track::play, which should be classified as a reference to an instance method of a particular object (see), the compiler will try to resolve it to a method play(Event) and you're getting a compilation error because it fails to find one.
In this case, reference track::play is not an equivalent of lambda e -> track.play(), but () -> track.play(), which doesn't conform to the target functional interface EventHandler.
In case if you wonder, how a method reference which can be applicable to a non-static method of arity n-1 mentioned in the specification (see case ii) can look like, here is an example:
BiPredicate<String, String> startsWith = String::startsWith; // the same as (str1, str2) -> str1.strarsWith(str2);
System.out.println(startsWith.test("abc", "a")); // => true
System.out.println(startsWith.test("fooBar", "a")); // => false
And you can construct a similar reference which conforms to EventHandler interface and applicable an instance method of arity n-1 using one of the parameterless methods of the Event type. It's not likely to be useful in practice, but it would be valid from the compiler perspective of view, so feel free to try it as an exercise.
I am studying for the OCP exam and I noticed a following snippet of code, where the parameter to the forEach method invoked on a DoubleStream must match that of the DoubleConsumer functional interface, however the lambda does not match the required types, why does it still compile?
DoubleStream.of(3.14159, 2.71828)
.forEach(c -> service.submit(
() -> System.out.println(10*c)
));
DoubleConsumer (accepts an argument of type Double and has a return type of void), however this forEach has a return type of Future<?> where ? denotes the return type of the Runnable lambda which is void, Future - this is not void. I am saying this because the return type of service.submit(...) is Future<?> it is not void, why does this code compile?
It is not really true that the return type of the lambda expression and the return type of the function type of the target functional interface type has to exactly match. The Java Language Specification specifies that void is a special case.
In §15.27.3, it says:
A lambda expression is compatible in an assignment context, invocation context, or casting context with a target type T if T is a functional interface type (§9.8) and the expression is congruent with the function type of the ground target type derived from T.
We are in an invocation context here. T is DoubleConsumer. The ground target type derived from it is also DoubleConsumer, and its function type is a method that takes a double and returns void.
Let's see what does "congruent" mean:
A lambda expression is congruent with a function type if all of the following are true:
[...]
If the lambda parameters are assumed to have the same types as the function type's parameter types, then:
If the function type's result is void, the lambda body is either a statement expression (§14.8) or a void-compatible block.
"assumed to have the same types as the function type's parameter types" basically means that you did not explicitly write out the type of c.
A statement expression is just an expression that can be made into a statement by adding a ; at the end. Any method call is a statement expression. This is why the submit call compiles.
5 is not a statement expression (but it is an expression), so c -> 5 does not compile.
If you think about it, by saying that methods that returns something should not be assigned to functional interfaces whose function type has a void return type, you are saying that "functions that take a A and gives out a B" are not a kind of "consumers of A". However, they clearly are "consumers of A"! They take in an A after all. Whether or not something is a consumer of A doesn't depend on what they produce.
Hence, Java is designed to allow this.
I am getting an error on the following code, which I believe should not be there... Using JDK 8u40 to compile this code.
public class Ambiguous {
public static void main(String[] args) {
consumerIntFunctionTest(data -> {
Arrays.sort(data);
}, int[]::new);
consumerIntFunctionTest(Arrays::sort, int[]::new);
}
private static <T> void consumerIntFunctionTest(final Consumer<T> consumer, final IntFunction<T> generator) {
}
private static <T> void consumerIntFunctionTest(final Function<T, ?> consumer, final IntFunction<T> generator) {
}
}
The error is the following:
Error:(17, 9) java: reference to consumerIntFunctionTest is ambiguous
both method consumerIntFunctionTest(java.util.function.Consumer,java.util.function.IntFunction) in net.tuis.ubench.Ambiguous and method consumerIntFunctionTest(java.util.function.Function,java.util.function.IntFunction) in net.tuis.ubench.Ambiguous match
The error occurs on the following line:
consumerIntFunctionTest(Arrays::sort, int[]::new);
I believe there should be no error, as all Arrays::sort references are of type void, and none of them return a value. As you can observe, it does work when I explicitly expand the Consumer<T> lambda.
Is this really a bug in javac, or does the JLS state that the lambda cannot automatically be expanded in this case? If it is the latter, I would still think it is weird, as consumerIntFunctionTest with as first argument Function<T, ?> should not match.
In your first example
consumerIntFunctionTest(data -> {
Arrays.sort(data);
}, int[]::new);
the lambda expression has a void-compatible block which can be identified by the structure of the expression without the need to resolve the actual types.
In contrast, in the example
consumerIntFunctionTest(Arrays::sort, int[]::new);
the method reference has to be resolved to find out, whether it conforms to either, a void function (Consumer) or a value returning function (Function). The same applies to the simplified lambda expression
consumerIntFunctionTest(data -> Arrays.sort(data), int[]::new);
which could be both, void- compatible or value- compatible, depending on the resolved target method.
The problem is that resolving the method requires knowledge about the required signature, which ought to be determined via target typing, but the target type isn’t known until the type parameters of the generic method are known. While in theory both could be determined at once, the (still being awfully complex) process has been simplified in the specification in that method overload resolution is performed first and type inference is applied last (see JLS §15.12.2). Hence, the information that type inference could provide cannot be used for solving overload resolution.
But note that the first step described in 15.12.2.1. Identify Potentially Applicable Methods contains:
An expression is potentially compatible with a target type according to the following rules:
A lambda expression (§15.27) is potentially compatible with a functional interface type (§9.8) if all of the following are true:
The arity of the target type's function type is the same as the arity of the lambda expression.
If the target type's function type has a void return, then the lambda body is either a statement expression (§14.8) or a void-compatible block (§15.27.2).
If the target type's function type has a (non-void) return type, then the lambda body is either an expression or a value-compatible block (§15.27.2).
A method reference expression (§15.13) is potentially compatible with a functional interface type if, where the type's function type arity is n, there exists at least one potentially applicable method for the method reference expression with arity n (§15.13.1), and one of the following is true:
The method reference expression has the form ReferenceType :: [TypeArguments] Identifier and at least one potentially applicable method is i) static and supports arity n, or ii) not static and supports arity n-1.
The method reference expression has some other form and at least one potentially applicable method is not static.
…
The definition of potential applicability goes beyond a basic arity check to also take into account the presence and "shape" of functional interface target types. In some cases involving type argument inference, a lambda expression appearing as a method invocation argument cannot be properly typed until after overload resolution.
So your in first example one of the methods is sorted out by the lambda’s shape while in case of a method reference or a lambda expression consisting of a sole invocation expression, both potentially applicable methods endure this first selection process and yield an “ambiguous” error before type inference can kick in to aid finding a target method to determine if it’s a void or value returning method.
Note that like using x->{ foo(); } to make a lambda expression explicitly void-compatible, you can use x->( foo() ) to make a lambda expression explicitly value-compatible.
You may further read this answer explaining that this limitation of combined type inference and method overload resolution was a deliberate (but not easy) decision.
With method references, you could have entirely different parameter types, let alone return types, and still get this if you have another method where the arity (number of arguments) matches.
For example:
static class Foo {
Foo(Consumer<Runnable> runnableConsumer) {}
Foo(BiFunction<Long, Long, Long> longAndLongToLong) {}
}
static class Bar {
static void someMethod(Runnable runnable) {}
static void someMethod(Integer num, String str) {}
}
There's no way Bar.someMethod() could ever satisfy longAndLongToLong, and yet the code below emits the same compile error regarding ambiguity:
new Foo(Bar::someMethod);
Holger's answer explains the logic and pertinent clause in the JLS behind this rather well.
What about binary compatibility?
Consider if the longAndLongToLong version of Foo constructor didn't exist but was added later in a library update, or if the two parameter version of Bar.someMethod() didn't exist but added later: Suddenly previously compiling code can break due to this.
This is an unfortunate side-effect of method overloading and similar problems have affected plain method calls even before lambdas or method references came along.
Fortunately, binary compatibility is preserved. The relevant clause is in 13.4.23. Method and Constructor Overloading:
Adding new methods or constructors that overload existing methods or constructors does not break compatibility with pre-existing binaries. The signature to be used for each invocation was determined when these existing binaries were compiled; ....
While adding a new overloaded method or constructor may cause a compile-time error the next time a class or interface is compiled because there is no method or constructor that is most specific (§15.12.2.5), no such error occurs when a program is executed, because no overload resolution is done at execution time.
This question already has answers here:
Which overload will get selected for null in Java?
(3 answers)
Closed 9 years ago.
I have the following code snippet:
public static void foo(Object x) {
System.out.println("Obj");
}
public static void foo(String x) {
System.out.println("Str");
}
If I call foo(null) why is there no ambiguity? Why does the program call foo(String x) instead of foo(Object x)?
why the program calls foo(String x) instead of foo(Object x)
That is because String class extends from Object and hence is more specific to Object. So, compiler decides to invoke that method. Remember, Compiler always chooses the most specific method to invoke. See Section 15.12.5 of JLS
If more than one member method is both accessible and applicable to a
method invocation, it is necessary to choose one to provide the
descriptor for the run-time method dispatch. The Java programming
language uses the rule that the most specific method is chosen.
The informal intuition is that one method is more specific than
another if any invocation handled by the first method could be passed
on to the other one without a compile-time type error.
However, if you have two methods with parameter - String, and Integer, then you would get ambiguity error for null, as compiler cannot decide which one is more specific, as they are non-covariant types.
The type of null is by definition a subtype of every other reference type. Quote JLS 4.1:
The null reference can always undergo a widening reference conversion to any reference type.
The resolution of the method signature involved in an invocation follows the principle of the most specific signature in the set of all compatible signatures. (JLS 15.12.2.5. Choosing the Most Specific Method).
Taken together this means that the String overload is chosen in your example.
It's calling the most specific method.
Since String is a subclass of Object, String is "more specific" than Object.
When given a choice between two methods where the argument is valid for both parameters, the compiler will always choose the most specific parameter as a match. In this case, null is a literal that can be handled as an Object and a String. String is more specific and a subclass of Object so the compiler uses it.
This question already has answers here:
Which overload will get selected for null in Java?
(3 answers)
Closed 9 years ago.
I have the following code snippet:
public static void foo(Object x) {
System.out.println("Obj");
}
public static void foo(String x) {
System.out.println("Str");
}
If I call foo(null) why is there no ambiguity? Why does the program call foo(String x) instead of foo(Object x)?
why the program calls foo(String x) instead of foo(Object x)
That is because String class extends from Object and hence is more specific to Object. So, compiler decides to invoke that method. Remember, Compiler always chooses the most specific method to invoke. See Section 15.12.5 of JLS
If more than one member method is both accessible and applicable to a
method invocation, it is necessary to choose one to provide the
descriptor for the run-time method dispatch. The Java programming
language uses the rule that the most specific method is chosen.
The informal intuition is that one method is more specific than
another if any invocation handled by the first method could be passed
on to the other one without a compile-time type error.
However, if you have two methods with parameter - String, and Integer, then you would get ambiguity error for null, as compiler cannot decide which one is more specific, as they are non-covariant types.
The type of null is by definition a subtype of every other reference type. Quote JLS 4.1:
The null reference can always undergo a widening reference conversion to any reference type.
The resolution of the method signature involved in an invocation follows the principle of the most specific signature in the set of all compatible signatures. (JLS 15.12.2.5. Choosing the Most Specific Method).
Taken together this means that the String overload is chosen in your example.
It's calling the most specific method.
Since String is a subclass of Object, String is "more specific" than Object.
When given a choice between two methods where the argument is valid for both parameters, the compiler will always choose the most specific parameter as a match. In this case, null is a literal that can be handled as an Object and a String. String is more specific and a subclass of Object so the compiler uses it.