-1

I have a AbstractBaseRepository. All my Repositories extends from this class. I created another class RepositoryFactory to create any instance of Repository. Due to early binding of static method, I am facing problem.

public abstract class AbstractBaseRepository {
    public static <T extends AbstractBaseRepository> T getNewInstance(EntityManagerFactory entityManagerFactory) {
        throw new RuntimeException("Override and provide valid initialization");
    }
    ...
}

public class RepositoryFactory {
    public static <T extends AbstractBaseRepository>  T getRepository(Class<T> cls) {       
        return T.getNewInstance(entityManagerFactory);
    }
    ...
}

an example subclass

public class DeviceModelRepo extends AbstractBaseRepository {

    public static DeviceModelRepo getNewInstance(EntityManagerFactory entityManagerFactory) {
        return new DeviceModelRepo(entityManagerFactory);
    }
    ...
}

Whenever I call getRepository() with a valid subclass of AbstractBaseRepository, runtime exception is thrown. This is due to early binding of static methods. During compile time, getNewInstance gets bound with AbstractBaseRepository rather than at runtime with actual type of the class. Any good workarounds?

Saikat Dey
  • 21
  • 3
  • *early binding of static methods*, dont use static and use proper `@Override` annotation? – Bagus Tesa Oct 29 '19 at 04:11
  • Looks, you are trying to override the static method in subclass but static method will never be overwritten. If you try to do in subclass, it will hide the parent static method and on whichever class you call it, it will call that class method. If you want to override the method then remove static keyword from it. – Ashok Prajapati Oct 29 '19 at 04:18
  • @BagusTesa if I use instance method and override, won't I need separate RepositoryFactory for each Repository? – Saikat Dey Oct 29 '19 at 04:25
  • @AshokPrajapati if I remove static keyword, my RepositoryFactory won't be able to initialize an instance. I will have to provide separate RepositoryFactory for each Repository – Saikat Dey Oct 29 '19 at 04:27
  • Hi @SaikatDey, what Ashok Prajapati means, is roughly something like this: https://repl.it/repls/LiveDotingSlope see how casting the variable **affect** which static method you call. thats why we suggested to use proper overriding approach (or just find a nice DI library to solve your problems).. – Bagus Tesa Oct 29 '19 at 04:37

2 Answers2

0

My first suggestion is using Spring. It is very easy to get a list of all beans created with a certain interface.

Also, if you think of your Repository instances as a type of "plug-in" you might see how Java's ServiceLoader class can help.

Also, another approach is to use a switch statement in the factory and create the instances for each case rather than using static methods on the Repository subclasses.

Finally, I don't recommend reflection solutions but there are ways to load the class based on its name and reflectively creating a new instance.

But overriding static methods is not possible.

fedup
  • 1,209
  • 11
  • 26
0

What I have understood by seeing your code is that you want to have different implementations of AbstractBaseRepository such as DeviceModelRepo. Then you want a factory class to create the instance of specific implementation of AbstractBaseRepository. Here the major problem is you try to overriding static methods which can never be overwritten but subclass will hide the parent implementation. Please don't use static method for overriding. You can change your implementation as given below and this issue will be resolved.

public abstract class AbstractBaseRepository {
    public AbstractBaseRepository(EntityManagerFactory entityManagerFactory){
        ...
    }
    //removed method getNewInstance(EntityManagerFactory entityManagerFactory) 
    ...
}

Then below implementation for subclass.

public class DeviceModelRepo extends AbstractBaseRepository {

    public DeviceModelRepo(EntityManagerFactory entityManagerFactory) {
        super(entityManagerFactory);
        ...
    }
    //removed method getNewInstance(EntityManagerFactory entityManagerFactory) 
    ...
}

Now I am providing you two implementation of factory class. One is having different method for each of implementation, such as getDeviceModelRepository(). Another solution is to use reflection and get repository instance by passing the implementation repository class.

public class RepositoryFactory {
    //Solution-1, create separate method for each of repository like below
    public static AbstractBaseRepository getDeviceModelRepository() {       
        return new DeviceModelRepo(entityManagerFactory);
    }
    //Solution-2, use reflection to get instance of specific implementation
    //of AbstractBaseRepository
    public static <T extends AbstractBaseRepository> T 
        getRepository(Class<T> repoClass) throws Exception{

        return repoClass.getConstructor(EntityManagerFactory.class)
            .newInstance(entityManagerFactory);
    }
    ...
}

With reflection solution, you can get the repository instance as given below.

RepositoryFactory.getRepository(DeviceModelRepo.class)
Ashok Prajapati
  • 374
  • 2
  • 7
  • Thanks for the solution. I got what you are trying to do. I will either use Solution-1 or @fedup 's switch statement (Both are basically same). I wanted to separate the creation of individual repository's logic in their respective class. Guess that's not possible. Thanks :) – Saikat Dey Oct 29 '19 at 14:58
  • Please don't forget to vote or accept the answers if they are helpful to you. Thank you! – Ashok Prajapati Oct 29 '19 at 16:46