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?
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.
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 .
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.
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.
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With