【问题标题】:Get around early binding of static methods in java绕过java中静态方法的早期绑定
【发布时间】:2019-10-29 04:02:45
【问题描述】:

我有一个 AbstractBaseRepository。我所有的存储库都从这个类扩展。我创建了另一个类 RepositoryFactory 来创建 Repository 的任何实例。由于静态方法的早期绑定,我遇到了问题。

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);
    }
    ...
}

一个示例子类

public class DeviceModelRepo extends AbstractBaseRepository {

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

每当我使用 AbstractBaseRepository 的有效子类调用 getRepository() 时,都会引发运行时异常。这是由于静态方法的早期绑定。在编译期间,getNewInstance 与 AbstractBaseRepository 绑定,而不是在运行时与类的实际类型绑定。有什么好的解决方法吗?

【问题讨论】:

  • 提前绑定静态方法,不要使用static,使用正确的@Override注解?
  • 看起来,您正试图覆盖子类中的静态方法,但静态方法永远不会被覆盖。如果您尝试在子类中执行,它将隐藏父静态方法,并且在您调用它的任何类上,它都会调用该类方法。如果要覆盖该方法,请从中删除 static 关键字。
  • @BagusTesa 如果我使用实例方法并覆盖,我不需要为每个 Repository 单独的 RepositoryFactory 吗?
  • @AshokPrajapati 如果我删除 static 关键字,我的 RepositoryFactory 将无法初始化实例。我将不得不为每个 Repository 提供单独的 RepositoryFactory
  • 嗨@SaikatDey,Ashok Prajapati 的意思大致是这样的:repl.it/repls/LiveDotingSlope 看看如何将变量影响你调用的静态方法。这就是为什么我们建议使用适当的覆盖方法(或者只是找到一个不错的 DI 库来解决您的问题)..

标签: java static-binding


【解决方案1】:

我的第一个建议是使用 Spring。获取使用某个接口创建的所有 bean 的列表非常容易。

此外,如果您将 Repository 实例视为一种“插件”,您可能会看到 Java 的 ServiceLoader 类如何提供帮助。

另外,另一种方法是在工厂中使用 switch 语句并为每个案例创建实例,而不是在 Repository 子类上使用静态方法。

最后,我不推荐反射解决方案,但有一些方法可以根据其名称加载类并反射地创建一个新实例。

但无法覆盖静态方法。

【讨论】:

    【解决方案2】:

    通过查看您的代码,我了解到您希望拥有 AbstractBaseRepository 的不同实现,例如 DeviceModelRepo。然后你想要一个工厂类来创建 AbstractBaseRepository 的具体实现的实例。这里的主要问题是您尝试覆盖永远无法覆盖但子类将隐藏父实现的静态方法。请不要使用静态方法进行覆盖。您可以按如下所示更改您的实施,此问题将得到解决。

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

    下面是子类的实现。

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

    现在我为您提供两个工厂类的实现。 一种是对每个实现都有不同的方法,例如 getDeviceModelRepository()。 另一种解决方案是使用反射并通过传递实现存储库类来获取存储库实例。

    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);
        }
        ...
    }
    

    使用反射解决方案,您可以获得如下所示的存储库实例。

    RepositoryFactory.getRepository(DeviceModelRepo.class)
    

    【讨论】:

    • 感谢您的解决方案。我得到了你想要做的事情。我将使用 Solution-1 或 @fedup 的 switch 语句(两者基本相同)。我想在各自的类中分离创建单个存储库的逻辑。估计这是不可能的。谢谢:)
    • 如果对您有帮助,请不要忘记投票或接受答案。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-11-20
    • 1970-01-01
    • 2013-08-04
    • 2021-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多