简体   繁体   中英

Java hashCode for similar classes

I have two similar classes, each with a single field of the same type.

class Fruit {
    private final String name;
}

class Vegetable {
    private final String name;
}

I'd like to implement hashCode() for each. My problem is that in my case, collisions between names are somewhat more possible than with "apple" and "carrot," and they both might be in the same Map/Set. I'm wondering what's the most clear way of implementing hashCode to handle this.

So far, I've considered Objects.hash(this.getClass(), name) , Objects.hash(<some int unique to this class>, name) . I like the first just because it's a bit more self-documenting and robust than the second, but it's not a pattern I've seen in the wild. I also considered <some prime int unique to this class> * Objects.hashCode(name) , but that felt fragile, especially if a new field gets added.

Assuming the 2 classes extend a common parent class, I solved this by adding a second field that would tell the instances of two different classes apart. This may be regarded as just another way of using the class name mentioned by David Ehrmann . But in my case using an additional field looks more appropriate than using a class name. So here's my abstract parent class:

public abstract class NationalDish {
    public String dishName;
    public String country;

    @Override
    public int hashCode() {
        return Objects.hash(country, dishName);
    }
    
    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof NationalDish)) {
            return false;
        }
        NationalDish other = (NationalDish) obj;
        return Objects.equals(dishName, other.dishName) 
            && Objects.equals(country, other.country);
    }
}

Note how having the fields in the parent class allows to define equals() and hash code() in that same class and keep child classes to the minimum:

public class EnglishDish extends NationalDish {
    public EnglishDish(String dishName) {
        this.dishName = dishName;
        this.country = "England";
    }
}

public class AmericanDish extends NationalDish {
    public AmericanDish(String dishName) {
        this.dishName = dishName;
        this.country = "USA";
    }
}

Now, with country names (or plant types like in the question) in place we can have same name instances which will look different to Java:

public static void main(String[] args) {
    NationalDish englishChips = new EnglishDish("Chips");
    NationalDish americanChips = new AmericanDish("Chips");
    System.out.println(englishChips.equals(americanChips)); // false
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM