Java: How is Optional.empty () compilation used?
This seems like a really stupid question, but I can't figure out why this usage Optional<T>
compiles:
import java.util.Optional;
public class Driver {
static void foo(Optional<String> x) { }
public static void main() {
foo(Optional.empty());
}
}
Optional::empty
defined as returning to me Optional<T>
. Internally, the Driver::main
expression Optional.empty()
looks like it will return Optional<Object>
as I am not parameterizing usage Optional
, so I expect it to return to Object
as a type parameter. Then I pass the Optional<Object>
function awaiting Optional<String>
, which is a descending parameter that shouldn't be allowed. I would expect to see something like:
incompatible types: Optional<Object> cannot be converted to Optional<String>
However, the code compiles just fine. Clearly my thought process is wrong here ... but where?
Let me clarify what I'm looking for in the answer here ... I know what an output type is. I don't understand how this is happening here and what has changed in Java 7 language to Java 8. For example, this bit of code compiles fine in Java 8, but does not work in Java 7:
final class Opt<T> {
private final T value;
Opt(T x) {
value = x;
}
public static <T> Opt<T> empty() {
return new Opt<T>(null);
}
}
public class Driver {
static void bar(Opt<String> x) { }
public static void main() {
bar(Opt.empty());
}
}
How does this work in Java 8 when you have to deal with things like overloading? Is there a specific section of the Java language spec that talks about this?
source to share
This is because of the way the empty () method is defined in Optional:
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
Notice the type parameter of the method above:
public static<T> Optional<T> empty() {
^^^ method type parameter
This means that when you call empty () it will bind T to the context of its caller, in your case String. For more information, see the Target Types section of this page in the Java Tutorial:
http://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html
source to share
Since this part of your question has not been resolved, I will try to summarize what changed between Java 7 and Java 8.
Java 7 already had type inference, eg. You can write
List<String> list=Collections.emptyList();
or
List<String> getList() {
return Collections.emptyList();
}
But this type of inference was pretty limited, for example. what didn't work (apart from others):
List<String> list=Collections.unmodifiableList(Collections.emptyList());
or
List<String> getList() {
return condition? new ArrayList<>(): Collections.emptyList();
}
These two examples now work in Java 8. This new feature is called object-type inference because it now uses the target type to find matching type arguments. Besides the fact that nested calls and nested method conditions work as in the above examples, it also fixes the following example:
List<Number> numbers=Arrays.asList(1, 2, 3, 4);
As said, Java 7 also has type inference, but in this example, it will infer List<Integer>
as a result the type of the expression from the arguments passed to asList
and hence generate an error.
Unlike Java 8 has target type deduction and will use target type deduction List<Number>
as expression type and find out that the whole statement is valid as you can use objects Integer
where Number
.
Note that Optional.empty()
both Collections.emptyList()
use the same type of general design. I used the latter in my examples as it already exists in Java 7.
source to share
This is because the compiler does type inference.
When the compiler reads:
static void foo(Optional<String> x) { }
public static void main() {
foo(Optional.empty());
}
- It knows what it
Optional.<T>empty()
takesT
as a parameter. - He knows what to
foo
expectOptional<String>
- This means that
T
String
.
This was introduced when generics were introduced, and its mechanism may have been improved with Java 8 javac
.
Note that this is compiler dependent: ECJ (Eclipse JDT Compiler) does not understand the same java source that Javac does (they compile the "same" bytecode, however).
source to share
Java 8 added a type interface, which means it will work out the type of the expression based on how it is used.
An explicit example:
Object o = () -> System.out.println("Hello World");
does not compile because it does not know the type of the expression, however
Runnable r = () -> System.out.println("Hello World");
Object o = (Runnable) () -> System.out.println("Hello World");
compile fine. those. the type of expression changes due to how it is used.
source to share