Jim
Jim

Reputation: 4425

Using an interface as a key in a hashmap

I tried to use an interface as a key in a hashMap in order to have 1 map for multiple types of keys. The following seems to work.

interface Foo {
        void function();
}
static class Bar implements Foo {

      private int id;
    
      public Bar(int id) {
          this.id = id;
      }
    
      @Override
      public void function() {
          System.out.println("this is bar");
      }
    
      @Override
      public boolean equals(Object o) {
           if (this == o) return true;
           if (o == null || getClass() != o.getClass()) return false;
           Bar bar = (Bar) o;
           return id == bar.id;
       }
    
       @Override
       public int hashCode() {
          return Objects.hash(id);
       }
}
    
public static Map<Foo, Integer> map = new HashMap<>();
    
    
     
static class Baz implements Foo {
    String name;
    public Baz(String name) {
        this.name = name;
    }

    @Override
    public void function() {
       System.out.println("this is Baz");
    }
    @Override
    public boolean equals(Object o) {
       if (this == o) return true;
       if (o == null || getClass() != o.getClass()) return false;
       Baz baz = (Baz) o;
       return name.equals(baz.name);
    }

    @Override
    public int hashCode() {
       return Objects.hash(name);
    }
}

public static void main(String[] args) {
    Bar bar = new Bar(123);
    Baz baz = new Baz("some name");

    map.put(bar, 10);
    map.put(baz, 20);
  
    System.out.println(map.get(bar));
 }

What I am not sure about is if there is some corner case that would break this map?
Is there a case that having an interface as a key would break down? Could I have done it simpler using generics?

Upvotes: 1

Views: 2403

Answers (2)

Joni
Joni

Reputation: 111389

The only thing that's slightly out of the ordinary is that the equals methods have to compare Bar and Baz objects. When a Map only has one type of objects, the check this.getClass() == that.getClass in equals method never returns false. You have implemented this correctly though, so you don't have anything to worry about.

You may get more hash collisions than you expect. Imagine you have two classes that both have an int id field and implement hashCode with Objects.hash(id) - now objects of different classes with the same ID have the same hash code. If this use case is expected, you can perturb the hash in a way unique to each class, for example by mixing a class-specific constant to the hash:

@Override
public int hashCode() {
   return Objects.hash(1, id);
}

...

@Override
public int hashCode() {
   return Objects.hash(2, name);
}

Upvotes: 2

Andrew Vershinin
Andrew Vershinin

Reputation: 1968

In theory there could be problems with potentially more hash collisions leading to bad performance due to different implementations of hashCode, so you need to be careful, and test it with the real data. Other than that it is a valid use case.

Upvotes: 1

Related Questions