I am currently learning about the functionalities of the Optional class, and I am trying to build a simplified version of the Optional class. I was able to code ifPresent()
, filter()
, of()
, map()
and so on. However, I am currently stuck with the implementing or()
.
I know that or()
have the signature Optional<T> or(Supplier<? extends Optional<? extends T>> supplier)
. However, my implementation assumed that I can access the contents of the Optional. As show below:
class Optional<T> {
private final T item;
...
Optional<T> or(Supplier<? extends Optional<? extends T>> supplier) {
if (this.item == null) {
T item = supplier.get().item;
return Maybe.<T>of(item);
} else {
return this;
}
}
}
As you can see, T item = supplier.get().item
would throw an error saying that .item
is inaccessible due to it being private. How am I able to access the item
without causing this error?
First, you need to recall that you can not access a private
field through an instance of a subtype, even though assigning the subtype reference to a variable of the current type, which allows the access, is possible without cast.
So if you have
public class ClassWithPrivateField {
private String field;
static class Subclass extends ClassWithPrivateField {}
void someMethod(Subclass obj) {
String s = obj.field; // does not work, you can't access field through Subclass
}
}
you may write
public class ClassWithPrivateField {
private String field;
static class Subclass extends ClassWithPrivateField {}
void someMethod(Subclass obj) {
ClassWithPrivateField withBroaderType = obj; // always works
String s = withBroaderType.field; // now, no problem to access field
}
}
Now to your more complicated generic variant. If you have
public class Optional<T> {
private final T item;
private Optional(T t) {
item = t;
}
Optional<T> or(Supplier<? extends Optional<? extends T>> supplier) {
if(this.item == null) {
T item = supplier.get().item;
return Optional.of(item);
}
else return this;
}
private static <T> Optional<T> of(T item2) {
return new Optional<>(item2);
}
}
the access to item
is rejected by the compiler because the type returned by the supplier is ? extends Optional<? extends T>
which is a subtype of Optional<? extends T>
, just the same way as Subclass
is a subtype of ClassWithPrivateField
.
You can fix the issue the same way, by introducing a variable:
public class Optional<T> {
private final T item;
private Optional(T t) {
item = t;
}
Optional<T> or(Supplier<? extends Optional<? extends T>> supplier) {
if(this.item == null) {
Optional<? extends T> optional = supplier.get(); // valid assignment
T item = optional.item; // valid access
return Optional.of(item);
}
else return this;
}
private static <T> Optional<T> of(T item2) {
return new Optional<>(item2);
}
}
Alternatively, you could insert a type cast to Optional<? extends T>
like
T item = ((Optional<? extends T>)supplier.get()).item;
but I would prefer the variant with a variable as it immediately shows to the reader that the assignment (without a cast) is a valid type transition which can never fail. The type cast can not fail either and is a no-op at runtime, but its syntax is indistinguishable from type casts performing a runtime check that could fail.