61. Tackling type patterns for instanceof and generics
Consider the following snippet of code that uses instanceof
in the old-school fashion:
public static <K, V> void process(Map<K, ? extends V> map) {
if (map instanceof EnumMap<?, ? extends V>) {
EnumMap<?, ? extends V> books
= (EnumMap<?, ? extends V>) map;
if (books.get(Status.DRAFT) instanceof Book) {
Book book = (Book) books.get(Status.DRAFT);
book.review();
}
}
}
// use case
EnumMap<Status, Book> books = new EnumMap<>(Status.class);
books.put(Status.DRAFT, new Book());
books.put(Status.READY, new Book());
process(books);
As we know from Problem 56, we can combine instanceof
with generic types via unbounded wildcards, such as our EnumMap<?, ? extends V>
(or EnumMap<?, ?>
, but not EnumMap<K, ? extends V>
, EnumMap<K, ?>
, or EnumMap<K, V>
).
This code can be written more concisely via the type pattern for instanceof
as follows:
public static <K, V> void process(Map<K, ? extends V> map) {
if (map instanceof EnumMap<?, ? extends V> books
&& books.get(Status.DRAFT) instanceof Book book) {
book.review();
}
}
In the example based on plain instanceof
, we can also replace EnumMap<?, ? extends V>
with Map<?, ? extends V>
. But, as we know from Problem 53, this is not possible with type patterns because the expression type cannot be a subtype of pattern type (upcasting is allowed). However, this is not an issue anymore starting with JDK 21.