2

I have a class hierarchy, where, say, class ICommon is at the root. I have separate repositories implementations in order to manipulate objects belonging at each level in this hierarchy.

interface IRepository<T extends ICommon> {
   public void createOrUpdate(T toCreate);
}

Now, I want to maintain a Map of classes VS their repositories. Its this part which I can't figure out.

I tried this, which works, but I do not think it is the correct representation.

private Map<Class<? extends ICommon>, IRepository<? extends ICommon>> repoMap = new   
   HashMap<Class<? extends ICommon>, IRepository<? extends ICommon>>();

Later on, clients call this method to get an appropriate repository instance.

public <T extends ICommon> IRepository<T> getRepository(Class<T> clazz) {
   return (IRepository<T>) repoMap.get(clazz);
}
Sourajit Basak
  • 693
  • 5
  • 15

1 Answers1

1

I faced similar problem some time ago and generally there is not way to restrict key and value in map, however it is possible to construct map in typesafe way. If you introduce one more method for returning target class repository you'll get compile time checking(unfortunately you can't do this in runtime since generic type is raised). After you have this method you can register all repositories. It could be either implicit way - when repository holder know about all repositories, or by exploring classpath for classes with IRepository interface implemented, or if you use IoC container (like spring) you can get those classes autowired. Anyway, plain java solution is below:

interface IRepository<T extends ICommon> {
    public void createOrUpdate(T toCreate);

    public Class<T> getTargetClass();
}

class Repository1 implements IRepository<Common1> {

    @Override
    public void createOrUpdate(Common1 toCreate) {
        //no-op
    }

    @Override
    public Class<Common1> getTargetClass() {
        return Common1.class;
    }
}

class Repository2 implements IRepository<Common2> {

    @Override
    public void createOrUpdate(Common2 toCreate) {
        //no-op
    }

    @Override
    public Class<Common2> getTargetClass() {
        return Common2.class;
    }
}

class RepositoryHolder<T extends ICommon> {

    private IRepository repository1 = new Repository1();
    private IRepository repository2 = new Repository2();
    //...

    private Map<Class<T>, IRepository> map = new HashMap<Class<T>, IRepository>() {{
        put(repository1.getTargetClass(), repository1);
        put(repository2.getTargetClass(), repository2);
        //...
    }};

    public IRepository<T> getRepository(Class<T> clazz) {
        return map.get(clazz);
    }
}
Petro Semeniuk
  • 6,970
  • 10
  • 42
  • 65
  • I wouldn't rule out this solution, but somehow the constraints aren't getting reflected in the Map. – Sourajit Basak Feb 14 '12 at 06:14
  • That's right. You can't (to be more precise I wasn't able) reflect constraints in the Map. However you're getting compile time checking on Repository construction stage(because of generic return type of getTargetClass method). It was good enough for me. Anyway, if you find better solution (at least solution which doesn't require additional one more method used only at map construction time) please share it - I'll use it for in my case as well. – Petro Semeniuk Feb 15 '12 at 01:36
  • Sure, I will. But I think the way Map is defined is standing in our way to an elegant solution. If you were to check the source for Map, you will find methods like remove(.. get(.. which aren't generic. Folks at Sun claimed it was to provide backward compatibility. – Sourajit Basak Feb 15 '12 at 03:57