11

前几天我偶然发现了一些使用不便,java.util.ServiceLoader并且在我心中形成了一些问题。

假设我有一个通用服务:

public interface Service<T> { ... }

我无法明确告诉ServiceLoader加载具有特定泛型类型的实现。

ServiceLoader<Service<String>> services = 
   ServiceLoader.load(Service.class); // Fail.

我的问题是:有哪些合理的方法可以ServiceLoader安全地加载通用服务的实现?


在提出上述问题之后,在 Paŭlo 回答之前,我设法想出了一个解决方案。

public interface Service<T> { ...
    // true if an implementation can handle the given `t' type; false otherwise.
    public boolean canHandle(Class<?> t) { ...

public final class StringService implements Service<String> { ...
    @Override public boolean canHandle(Class<?> t) {
        if (String.class.isAssignableFrom(type)) 
            return true;
        return false;
    }

public final class DoubleService implements Service<Double> { ...
    // ...

public final class Services { ...
    public static <T> Service<T> getService(Class<?> t) {
        for (Service<T> s : ServiceLoader.load(Service.class))
            if (s.canServe(t))
                return s;
        throw new UnsupportedOperationException("No servings today my son!");
    }

更改boolean canServe(Class<?> t)boolean canServe(Object o)<T> Service<T> getService(Class<?> t)以相同的方式更改可能更具动态性(我自己使用后者,因为一boolean canHandle(T t)开始我的界面上有一个方法。)

4

2 回答 2

7

这里的问题是服务加载器正在使用一个列出给定类/接口的所有实现的文件,该文件由接口名称命名。没有预见到将类型参数放入该文件名中,并且实际上也不可能将泛型类型作为Class对象传递。

因此,您在这里只能获取任何类型的通用服务,然后检查它们的类对象以查看它是否是Service<String>.

像这样的东西:

class Test{

    public Service<String> getStringService() {
        // it is a bit strange that we can't explicitely construct a
        // parametrized type from raw type and parameters, so here
        // we use this workaround. This may need a dummy method or
        // variable if this method should have another return type.
        ParametrizedType stringServiceType =
            (ParametrizedType)Test.class.getMethod("getStringService").getGenericReturnType();

        ServiceLoader<Service<?>> loader = ServiceLoader.load(Service<?>.class);
        for(Service<?> service : loader) {
           if(isImplementing(service.getClass(), stringServiceType)) {
              @SuppressWarnings("unchecked")
              Service<String> s = (Service)service;
              return s;
           }
        }
    }

    public boolean isImplementing(Class<?> candidate, ParametrizedType t) {
        for(Type iFace : candidate.getGenericInterfaces()) {
           if(iFace.equals(t)) {
              return true;
           }
           if(iFace instanceof ParametrizedType &&
              ((ParametrizedType)iFace).getRawType().equals(t.getRawType())) {
              return false;
           }
        }
        return false;
    }

}

这未经测试,可能需要扩展以搜索由我们的类直接实现的接口扩展的接口,以及由我们的(通用)超类实现的接口。

当然,这只能找到像这样的类

class Example implements Service<String> { ...}

不像

class Example<X> implements Service<X> { ... }

whereExample<String>可能是您的服务的有效实现。

于 2011-03-27T21:44:15.083 回答
0

您也可以只复制 ServiceLoader 类文件并从 load() 方法中删除泛型类型参数,使其始终有效。您只需要覆盖警告。

  public static <S> ServiceLoader load(final Class<S> service)
   {
      return load(service, Thread.currentThread().getContextClassLoader());
   }
于 2011-06-28T05:16:07.773 回答