Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: How does use of Optional.empty() compile?

Tags:

java

java-8

This seems like a really stupid question, but I can't understand why this use of 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 is defined as returning me an Optional<T>. Inside of Driver::main, the expression Optional.empty() seems like it would return an Optional<Object>, as I am not parameterizing the use of Optional so I would expect it to fall back to Object as the type parameter. Then, I'm passing the Optional<Object> to a function which expects an Optional<String>, which is a downcast of the parameter, which should not be allowed. I would expect to see something like:

incompatible types: Optional<Object> cannot be converted to Optional<String>

However, the code compiles perfectly fine. Clearly, my thought process here is incorrect...but where?


Let me clarify what I'm looking for in an answer here...I know what type inference is. What I don't understand how it is happening here and what changed in the language from Java 7 to Java 8. For example, this bit of code compiles perfectly fine in Java 8, but fails 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 specification that talks about this sort of thing?

like image 239
Travis Gockel Avatar asked Sep 30 '14 20:09

Travis Gockel


People also ask

What is the use of optional empty () in Java?

In Java, the Optional object is a container object that may or may not contain a value. We can replace the multiple null checks using the Optional object's isPresent method. The empty method of the Optional method is used to get the empty instance of the Optional class. The returned object doesn't have any value.

What does Optional empty () get () return?

Returns an empty Optional instance. Indicates whether some other object is "equal to" this Optional. If a value is present, and the value matches the given predicate, return an Optional describing the value, otherwise return an empty Optional .

How do you declare an empty optional in Java?

Optional<User> userOptional = Optional. ofNullable(user); If the argument passed to Optional. ofNullable() is non-null, then it returns an Optional containing the specified value, otherwise it returns an empty Optional.

What is the use of optional ()?

Optional is a container object used to contain not-null objects. Optional object is used to represent null with absent value. This class has various utility methods to facilitate code to handle values as 'available' or 'not available' instead of checking null values.


2 Answers

It's because of the way way the empty() method is defined in Optional:

public static<T> Optional<T> empty() {
    @SuppressWarnings("unchecked")
    Optional<T> t = (Optional<T>) EMPTY;
    return t;
}

Note the method type parameter above:

public static<T> Optional<T> empty() {
             ^^^ method type parameter

This means that when empty() is called, it will bind T to the context of its caller, in your case String. For more information, see the section on Target Types in this page in the Java Tutorial:

http://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html

like image 103
cogitos Avatar answered Nov 06 '22 22:11

cogitos


Since that part of your question has not been addressed, I will try to summarize what have changed between Java 7 and Java 8.

Java 7 already had type inference, e.g. you could write

List<String> list=Collections.emptyList();

or

List<String> getList() {
    return Collections.emptyList();
}

But this type inference was rather limited, e.g. what did not work (besides others) was:

List<String> list=Collections.unmodifiableList(Collections.emptyList());

or

List<String> getList() {
    return condition? new ArrayList<>(): Collections.emptyList();
}

These two examples work now under Java 8. This new feature is called target type inference as it now uses the target’s type to find the appropriate type arguments. Besides making nested method invocations and conditionals work, like in the examples above, it also fixes the following example:

List<Number> numbers=Arrays.asList(1, 2, 3, 4);

As said, Java 7 had type inference too, but in this example it would infer List<Integer> as the result type of the expression from the arguments passed to asList and hence, generate an error.

In contrast, Java 8 has target type inference and will use the target type of the assignment to infer List<Number> as the type of the expression and find out that the entire statement is valid as you can use Integer objects where Number is expected.

Note that Optional.empty() and Collections.emptyList() use the same kind of Generic construct. I used the latter in my examples as it already exists in Java 7.

like image 31
Holger Avatar answered Nov 06 '22 23:11

Holger