如何使用guava class在接口中实例映射?

How to use guava class to instance map in interface?

我有一个名为 RestClientInterface 的接口,它由抽象 RestClient class 实现,那些 class 由 SearchRestClient 和 IndexRestClient 扩展。这两个 classes 都是单例。我希望能够在接口静态方法getInstance 中实现。我决定像这里建议的那样使用 Guava 库:How can I implement abstract static methods in Java?.

我在接口中实现了两个静态方法:

public interface RestClientInterface {

    ClassToInstanceMap<RestClientInterface> classToInstanceMap = MutableClassToInstanceMap.create();

    static <T extends RestClientInterface> T getInstance(Class<T> type) {
        return classToInstanceMap.getInstance(type);
    }

    static <T extends RestClientInterface> void registerInstance(Class<T> type, T identity) {
        classToInstanceMap.putIfAbsent(type, identity);
    }
}

和下一个注册的实例都扩展 classes:

public class IndexRestClient extends RestClient {

    static {
        RestClientInterface.registerInstance(IndexRestClient.class, getInstance());
    }

    /**
     * IndexRestClient singleton.
     */
    private static IndexRestClient instance = null;

    private IndexRestClient() {}

    /**
     * Return singleton variable of type IndexRestClient.
     *
     * @return unique instance of IndexRestClient
     */
    private static IndexRestClient getInstance() {
        if (Objects.equals(instance, null)) {
            synchronized (IndexRestClient.class) {
                if (Objects.equals(instance, null)) {
                    instance = new IndexRestClient();
                    instance.initiateClient();
                }
            }
        }
        return instance;
   } 
}

接下来我这样称呼它:

IndexRestClient restClient = RestClientInterface.getInstance(IndexRestClient.class);

但每次我得到的都是空值。实例的静态注册不起作用,因为已注册实例的数组为空。我怎样才能正确实例化这两个 classes?

那是因为classToInstanceMap对象不是静态的,而你使用的方法是静态的。这意味着仅当您创建 class 的新实例时才会创建地图。只需将静态关键字添加到此对象即可。

经过我们的讨论,问题可能与 class IndexRestClient 的延迟加载有关,如果它在进一步使用之前仅在语句 RestClientInterface.getInstance(IndexRestClient.class) 中被引用,因为我是不确定对 IndexRestClient.class 的引用是否足以触发 class.

的 load/initialization

假设这里的问题确实是 class IndexRestClient 被延迟加载,你需要反转注册逻辑的控制,而不是IndexRestClient在注册表中注册自己,中间有一个“Registrar”来处理它。

但是在我看来,你的 RestClientInterface 的合同应该改变,因为它不知道你的 Rest 客户的具体类型,而我知道你想隐藏他们创建的实现。

您可能想看看 Java service loader 机制,它看起来很接近您想要做的事情,例如:

public final RestClients {
    private final ServiceLoader<RestClient> restClients = ServiceLoader.load(RestClient.class);

    public RestClient getClient(RestClientSpec spec) throws NoClientFoundForSpecException {
        for (RestClient client : restClients) {
            if (/* client matches your specification */) {
                return client;
            }
        }
        throw new NoClientFoundForSpecException(spec);
    }
}

Service Loader 模式可能提供比您实际想要的更多的封装和隐藏更多的实现(因为您显然使用的是具体类型),但我觉得它值得一提。