2013-06-07 2 views
7

У меня есть «странная» проблема с генериками Java.Метод класса generic класса 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

puhhh ... пожалуйста, формат описания ... :-) –

+0

Где 'T' объявлен в 'ServiceProvider.getService()'? – hertzsprung

ответ

5

В этом случае было бы лучше, если бы 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 генериков просто не позволит вам делать то, что вы хотите, и вы должны эффективно сообщить компилятору, что вы делаете на самом деле будет законным во время выполнения.

+0

'new HashMap <>()' работает только для Java 7 и выше. Для Java 6 и ниже вам понадобится новый HashMap , ServiceProvider >() ' –

+0

Кроме того, в вашем методе' register' отсутствует тип 'void' he return. –

+0

@RajeshJAdvani спасибо, я виню слишком много программирования в groovy :-) –

5

Метод подписи

public void register(Class clazz, ServiceProvider provider) 

Вы передаете два Class экземпляра здесь:

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

Вам необходимо пройти экземпляр класса, который реализует интерфейс ServiceProvider в качестве второго аргумента register() метод.

+0

Извините за неправильный тип. Я заменяю второй параметр новым FooServiceProvider(), и жалоба по-прежнему существует. –

0

Ваш метод подписи, как:

public void register(Class clazz, ServiceProvider provider) 

Но вы передаете два экземпляра класса в регистр-метод

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

Вы должны передать экземпляр класса, который реализует интерфейс ServiceProvider как второй аргумент метода.

Вы должны использовать общие типы и без сырых типов. например Класс, набранный с? вместо единственного класса

+0

сначала прочитайте ответы, которые уже существуют ... –

+0

sry Я не обновлял страницу, прежде чем делать свое сообщение. Но я предлагаю не использовать сырые типы, а другое сообщение - нет. – Holger

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

безопасным и еще наберется

+0

Используется необработанный тип «ServiceProvider» в последней строке «main». Но в этой реализации «ServiceProvider sp = registry.lookup (FooService.class); 'не будет компилироваться, потому что' lookup' не заставляет 'lookup (X.class)' быть типа 'ServiceProvider ' (только 'ServiceProvider ') –

+0

Без броска вы не можете получить ServiceProvider от ServiceProviderRegistry . это нелогичная задача. Родительский тип не может быть передан ребенку неявно. – Mikhail

+0

Это моя точка. ServiceProviderRegistry не должен быть параметризованным классом вообще, но методы регистрации и поиска должны быть общими и обеспечивать соответствие типов. –