This is quite expected. The substituted type should be matched, even if it is an object type.
public final class Optional<T> {
private static final Optional<?> EMPTY = new Optional<>();
private final T value;
private Optional() {
this.value = null;
}
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
Above is a fraction of actual Java Optional class.
<?> is identical to <? extends Object>
Since a new method should always have a concrete type, new Optional<>() is actually new Optional
Let us think about why the type of EMPTY was set Optional<?> and not Optioanl<Object>
Optional<?> opt1 = new Optional<Object>();
Optional<Object> opt2 = new Optional<Object>();
Optional<String> stropt1 = (Optional<String>)opt1; //OK
Optional<String> stropt1 = (Optional<String>)opt2; //Error!
To conclude, casting Optional<Object> to Optional is impossible. **Optional\
But, Optional<Object> -> Optional<?> -> Optional<T> is possible
How Generic type is actually compiled
Compiler checks source file with Generic type, and casts wherever is necessary
Compiler removes the generic type. Hence, the compiled class file (*.class) does NOT have any information about the generic type.