8

我有一个关于 Java 泛型的“奇怪”问题。

首先我列出我的代码:

服务类

package jse.generics.service;

public interface Service {

}

服务提供者.class

package jse.generics.service;

public interface ServiceProvider<T extends Service> {

    public T getService();
}

ServiceProviderRegistry.class

package jse.generics.service;

import java.util.HashMap;
import java.util.Map;

public class ServiceProviderRegistry<T extends Service> {

     private Map<Class<T>, ServiceProvider<T>> map = new HashMap<Class<T>, ServiceProvider<T>>();

     public void register(Class<T> clazz, ServiceProvider<T> provider) {
          map.put(clazz, provider);
     }
}

FooService.class

package jse.generics.service;

public class FooService implements Service {

}

FooServiceProvider.class

package jse.generics.service;

public class FooServiceProvider implements ServiceProvider<FooService> {

     @Override
     public FooService getService() {
          return new FooService();
     }

}

服务测试类

package jse.generics.service;

public class ServiceTest {

     /**
     * @param args
     */
     public static void main(String[] args) {
          ServiceProviderRegistry<? extends Service> registry = new ServiceProviderRegistry<Service>();
          registry.register(FooService.class, new FooServiceProvider());
     }

}

在 ServiceTest 类中,编译器抱怨 registry.register 方法不适用于传递给它的参数。我真的不知道为什么会发生这种情况。所以我期待着你的帮助来解决这个问题。

4

4 回答 4

6

ServiceProviderRegistry在这种情况下,如果不是参数化类,你会更好,而是使register方法(以及相应的查找方法)通用。在您当前的方法中,只能ServiceProviderRegistry<Service>注册,而不是任何服务子类。Service.class

您真正关心的是传递的类和提供者是否register相互匹配,这是泛型方法的理想情况。

public class ServiceProviderRegistry {
  private Map<Class<?>, ServiceProvider<?>> registry = new HashMap<>();

  public <T extends Service> void register(Class<T> cls, ServiceProvider<T> provider) {
    registry.put(cls, provider);
  }

  @SuppressWarnings("unchecked")
  public <T extends Service> ServiceProvider<T> lookup(Class<T> cls) {
    return (ServiceProvider<T>)registry.get(cls);
  }
}

您将需要@SuppressWarnings注释 - 如果没有注释,就不可能以完全满足编译器的方式实现此模式,编译器只能访问编译时类型。在这种情况下,您知道强制转换在运行时将始终是安全的,因为register它是唯一修改registry地图的东西,所以这@SuppressWarnings是合理的。 Jon Skeet 对这个相关问题的回答很好地总结了它

有时 Java 泛型只是不允许您做您想做的事情,您需要有效地告诉编译器您正在做的事情在执行时确实是合法的。

于 2013-06-07T08:22:05.937 回答
5

方法签名是

 public void register(Class clazz, ServiceProvider provider)

您在Class这里传递了两个实例:

registry.register(FooService.class, FooServiceProvider.class);

您需要将实现ServiceProvider接口的类的实例作为方法的第二个参数传递register()

于 2013-06-07T08:02:29.167 回答
0

您的方法签名如下:

 public void register(Class clazz, ServiceProvider provider)

但是您将两个 Class 实例传递给 register-Method

registry.register(FooService.class, FooServiceProvider.class);

您必须将实现 ServiceProvider 接口的类的实例作为该方法的第二个参数传递。

您应该使用泛型类型而不是原始类型。例如用 ? 键入的类 而不仅仅是类

于 2013-06-07T08:07:41.647 回答
0

奇怪,奇怪的泛型。这是我的解决方案:

public class ServiceProviderRegistry<T extends Service> {

    private Map<Class<? extends T>, ServiceProvider<? extends T>> map = new HashMap<Class<? extends T>, ServiceProvider<? extends T>>();

    public void register(Class<? extends T> clazz, ServiceProvider<? extends T> provider) {
        map.put(clazz, provider);
    }

    public ServiceProvider<? extends T> lookup(Class<? extends T> cls) {
        return map.get(cls);
    }
}

主要:

public static void main(String[] args) {
    ServiceProviderRegistry<Service> registry = new ServiceProviderRegistry<Service>();
    registry.register(FooService.class, new FooServiceProvider());
    ServiceProvider sp = registry.lookup(FooService.class);
    Service s = sp.getService(); //safe!
}

安全且仍在输入

于 2013-06-07T09:41:21.233 回答