Get around early binding of static methods in java

105 Views Asked by At

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?

2

There are 2 best solutions below

0
On BEST ANSWER

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.

2
On

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)