我对Java泛型有一个“奇怪”的问题。
首先我列出我的代码:
Service.class
package jse.generics.service;
public interface Service {
}
ServiceProvider.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();
}
}
ServiceTest.class
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方法不适用于传递给它的参数。我真的不知道为什么会发生这种情况。所以我期待着你帮助解决这个问题。
答案 0 :(得分: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's answer to this related question非常好地总结了
有时Java泛型不会让你做你想做的事情,你需要有效地告诉编译器你在执行时真正做的事情是合法的。
答案 1 :(得分:5)
方法签名是
public void register(Class clazz, ServiceProvider provider)
您在这里传递了两个Class
个实例:
registry.register(FooService.class, FooServiceProvider.class);
您需要将实现ServiceProvider
接口的类的实例作为register()
方法的第二个参数传递。
答案 2 :(得分:0)
您的方法签名如下:
public void register(Class clazz, ServiceProvider provider)
但是你将两个Class实例传递给register-Method
registry.register(FooService.class, FooServiceProvider.class);
您必须将实现ServiceProvider接口的类的实例作为方法的第二个参数传递。
您应该使用泛型类型而不使用原始类型。例如 上课类型? 而不仅仅是Class
答案 3 :(得分: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!
}
安全且仍然打字