Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Canonical form of field

I'm studying Effective Java, Item 8 (Obey the general contract when overriding equals). It has been explained quite clearly by the author, but still some parts are not that much elaborated.

For this example, he considers a class CaseInsensitiveString defined as :

public final class CaseInsensitiveString {
    private final String s;

    public CaseInsensitiveString(String s) {
        if (s == null)
            throw new NullPointerException();
        this.s = s;
    }

    // Broken - violates symmetry!
    @Override
    public boolean equals(Object o) {
        if (o instanceof CaseInsensitiveString)
            return s.equalsIgnoreCase(((CaseInsensitiveString) o).s);
        if (o instanceof String) // One-way interoperability!
            return s.equalsIgnoreCase((String) o);
        return false;
    }
    // ... // Remainder omitted
}

In the end of the article, he says :

For some classes, such as CaseInsensitiveString above, field comparisons are more complex than simple equality tests. If this is the case, you may want to store a canonical form of the field, so the equals method can do cheap exact comparisons on these canonical forms rather than more costly inexact compar- isons. This technique is most appropriate for immutable classes (Item 15); if the object can change, you must keep the canonical form up to date.

I searched for this term and found that it basically means a standard representation of something, like absolute path without any symbolic links for a file in a directory. But I'm unable to understand the use of 'canonical' form for this class, which would help here. Any suggestions?

like image 914
gaurav jain Avatar asked Nov 30 '15 05:11

gaurav jain


1 Answers

I think in this particular example, a canonical form might be storing the lowercase or uppercase version of the String and doing comparisons on that.

private final String s;

public CaseInsensitiveString(String s) {
    //for real code probably use locale version
    this.s = s.toLowerCase();
}

This makes the equality comparison cheaper because we can do exact string comparisons instead of the more expensive equalsIgnoreCase

// Broken - violates symmetry!
@Override
public boolean equals(Object o) {
    if (o instanceof CaseInsensitiveString)
        return s.equals(((CaseInsensitiveString) o).s);
    if (o instanceof String) // One-way interoperability!
        return s.equals((String) o);
    return false;
}
like image 123
dkatzel Avatar answered Oct 18 '22 20:10

dkatzel