Rewriting equals() via type patterns for instanceof – Objects, Immutability, Switch Expressions, and Pattern Matching

55. Rewriting equals() via type patterns for instanceof

It is not mandatory to rely on instanceof for implementing the equals() method, but it is a convenient approach to write something as follows:

public class MyPoint {
  private final int x;
  private final int y;
  private final int z;
  public MyPoint(int x, int y, int z) {
    this.x = x;
    this.y = y;
    this.z = z;
  }
  @Override
  public boolean equals(Object obj) {
    if (this == obj) {
      return true;
    }
    if (!(obj instanceof MyPoint)) {
      return false;
    }
    final MyPoint other = (MyPoint) obj;
    return (this.x == other.x && this.y == other.y
         && this.z == other.z);          
  }     
}

If you are a fan of the previous approach for implementing equals() then you’ll love to rewrite it via type pattern for instanceof. Check out the following snippet:

@Override
public boolean equals(Object obj) {
  if (this == obj) {
    return true;
  }
  return obj instanceof MyPoint other
    && this.x == other.x && this.y == other.y
    && this.z == other.z;          
}

If MyPoint is generic (MyPoint<E>) then simply use a wildcard as follows (more details are available in the next problem):

return obj instanceof MyPoint<?> other
  && this.x == other.x && this.y == other.y
  && this.z == other.z;

Cool, right?!

56. 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 51, 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 (up-casting is allowed).

Leave a Reply

Your email address will not be published. Required fields are marked *

© 2024 nickshade Please read our Terms and Privacy Policy. We also use Cookies.