Java泛型类的方法不适用于传递的参数

Sam*_*ong 7 java generics

关于Java泛型,我有一个"奇怪"的问题.

首先我列出我的代码:

Service.class

package jse.generics.service;

public interface Service {

}
Run Code Online (Sandbox Code Playgroud)

ServiceProvider.class

package jse.generics.service;

public interface ServiceProvider<T extends Service> {

    public T getService();
}
Run Code Online (Sandbox Code Playgroud)

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);
     }
}
Run Code Online (Sandbox Code Playgroud)

FooService.class

package jse.generics.service;

public class FooService implements Service {

}
Run Code Online (Sandbox Code Playgroud)

FooServiceProvider.class

package jse.generics.service;

public class FooServiceProvider implements ServiceProvider<FooService> {

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

}
Run Code Online (Sandbox Code Playgroud)

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

}
Run Code Online (Sandbox Code Playgroud)

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

Ian*_*rts 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);
  }
}
Run Code Online (Sandbox Code Playgroud)

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

有时Java泛型不会让你做你想做的事情,你需要有效地告诉编译器你在执行时真正做的事情是合法的.


NIN*_*OOP 5

方法签名是

 public void register(Class clazz, ServiceProvider provider)
Run Code Online (Sandbox Code Playgroud)

你在Class这里传递两个实例:

registry.register(FooService.class, FooServiceProvider.class);
Run Code Online (Sandbox Code Playgroud)

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