The Java generic class method is not applicable to the arguments passed

I have a "weird" problem with Java generics.

First I listed my code:

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

In the ServiceTest class, the compiler complains that the registry.register method is not applicable for the arguments passed to it. I really don't know why this is happening. Therefore, I look forward to helping you solve this problem.

+7
source share
4 answers

In this case, you would be better off if ServiceProviderRegistry not a parameterized class, but instead used the register method (and, presumably, the corresponding search method). In your current approach, ServiceProviderRegistry<Service> can only register Service.class , not any service subclasses.

All you really need is that the class and provider passed to register match each other, which is an ideal example for a generic method.

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

You will need the @SuppressWarnings annotation - it is impossible to implement this template without one method that completely satisfies the compiler, which has access only to compilation time types. In this case, you know that the throw will always be safe at runtime, because register is the only thing that modifies the registry map, so @SuppressWarnings is justified. Jon Skeet the answer to this related question sums up very nicely

Sometimes Java generics simply do not allow you to do what you want, and you need to effectively tell the compiler that what you are doing will really be legal at runtime.

+5
source

Method signature

  public void register(Class clazz, ServiceProvider provider) 

Here you pass two instances of Class :

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

You need to pass an instance of the class that implements the ServiceProvider interface as the second argument to the register() method.

+5
source

Your method signature is similar:

  public void register(Class clazz, ServiceProvider provider) 

But you pass two class instances to register-Method

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

You need to pass an instance of the class that implements the ServiceProvider interface as the second argument to the method.

You should use generic types and not raw types. e.g. Class typed with? instead of class

0
source

Strange, weird generics. Here is my solution:

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

and main:

 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! } 

safe and still printed

0
source

All Articles