I am, specifically concerned with obeying the symmetry part of the general contract established in Object#equals(Object) where, given two non-null objects x and y, the result of x.equals(y) and y.equals(x) should be the same.
Suppose you have two classes, PurchaseOrder and InternationalPurchaseOrder where the latter extends the former. In the basic case, it makes sense that comparing an instance of each against the other shall return consistently false for x.equals(y) and y.equals(x) simply because a PurchaseOrder is not always an InternationalPurchaseOrder and therefore, the additional fields in InternationalPurchaseOrder objects will not be present in instances of PurchaseOrder.
Now, suppose you upcast an instance of InternationalPurchaseOrder.
PurchaseOrder order1 = new PurchaseOrder();
PurchaseOrder order2 = new InternationalPurchaseOrder();
System.out.println("Order 1 equals Order 2? " + order1.equals(order2));
System.out.println("Order 2 equals Order 1? " + order2.equals(order1));
We already established that the result should be symmetric. But, should the result be false for cases when both object contain the same internal data? I believe the result should be true regardless of the fact that one object has an extra field. Since by upcasting order2, access to fields in InternationalPurchaseOrder class is restricted, the result of the equals() method shall be the result of the call to the super.equals(obj).
If all I stated is true, the implementation of the equals method in InternationalPurchaseOrder should be something like this:
@Override
public boolean equals(Object obj) {
    
     if (!super.equals(obj)) return false;
     
     // PurchaseOrder already passed check by calling super.equals() and this object is upcasted
     InternationalPurchaseOrder other = (InternationalPurchaseOrder)obj;
     
        if (this.country == null) {
            if (other.country != null) return false;
        } else if (!country.equals(other.country)) return false;
        return true;
}
Assuming that country is the only field declared in this subclass.
The problem is in the super-class
@Override
public boolean equals (Object obj) {
    if (this == obj) return true;
    if (obj == null) return false;
    if (getClass() != obj.getClass()) return false;
    PurchaseOrder other = (PurchaseOrder) obj;
    if (description == null) {
        if (other.description != null) return false;
    } else if (!description.equals(other.description)) return false;
    if (orderId == null) {
        if (other.orderId != null) return false;
    } else if (!orderId.equals(other.orderId)) return false;
    if (qty != other.qty) return false;
    return true;
}
Because the two instances are not of the same class. And if instead I use getClass().isAssignableFrom(Class), symmetry is lost. The same is true when using instanceof. In the book, Effective Java, Joshua Bloch indirectly warn about overriding equals unnecessarily. In this case, however, it is necessary for the subclass to have an overridden equals for instances to compare the fields declared in the subclass.
This has gone long enough. Is this a case for a more complicated implementation of the equals function, or is this simply a case against upcasting?
CLARIFICATION: This question is not answered by the suggestions proposed by @Progman in the comments section below. This is not a simple case of overriding equals methods for subclasses. I think the code I posted here shows that I have done this correctly. This post is SPECIFICALLY about the expected result of comparing two objects when one is upcasted so that it behaves like an object of the super-class.
 
    