Контравариантный параметр типа в Java?

Я смотрю на перенос этой библиотеки С# на Java и Android

https://github.com/AndersMalmgren/SignalR.EventAggregatorProxy/tree/master/SignalR.EventAggregatorProxy.Client.DotNet

Я хочу портировать этот интерфейс

public interface IHandle<in t=""> where T : class
{
 void Handle(T message);
}
</in>

Он должен обрабатывать контравариантность, чтобы он мог обрабатывать такие сообщения, как IHandle т.д.

Пользователь библиотеки может добавлять подписчиков и когда приходит сообщение. Я проверяю, что обработчик делает

subscribers
 .OfType<ihandle<t>>();
</ihandle<t>

Возможно ли это с помощью Java-дженериков, или существует ли более "Java-способ" для этого?

1 ответ

Я предполагаю с subscribers.OfType<ihandle<t>>();</ihandle<t> вы хотите, чтобы все подписчики имели тип IHandle где T - тип сообщения, не так ли?

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

Предполагая, что у вас есть один дескриптор для типа/класса, вы можете сделать что-то вроде этого:

interface IHandle<t> {
 Class<t> getMessageType(); //needed to query the handle for the supported class
 void handle( T message );
}
</t></t>

Для создания реализаций этого интерфейса у вас будет несколько вариантов:

Создайте реализацию для каждого класса:

class SpecialMessageHandle implements IHandle<specialmessage> {
 public Class<specialmessage> getMessageType() { return SpecialMessage.class; }

 public handle( SpecialMessage message ) { ... }
}
</specialmessage></specialmessage>

Или передайте класс как параметр:

class Handle<t> implements IHandle<t> {
 Class<t> messageClass;

 public Handle( Class<t> msgClass ) { messageClass = msgClass; }

 public Class<t> getMessageType() { return messageClass; }

 public handle( T message ) { ... }
}
</t></t></t></t></t>

Тогда у вас, вероятно, будет класс сообщений карты для обработки класса и класса обработки для подписчиков (например, многоадресной рассылки Guava):

Map<class<?>, IHandle<!--?-->> handles = ...;
Multimap<ihandle<?>, Subscriber> subscribers = ...;
</ihandle<?></class<?>

Обратите внимание, что я использую Class <!--?--> Здесь, потому что я не знаю, есть ли общий класс или интерфейс для классов. Если у вас есть общий суперкласс или интерфейс X вы должны использовать Class <!--? extends X--> вместо этого Class <!--? extends X-->.

Кроме того, обратите внимание, что я использовал IHandle <!--?--> как в Java вы не можете определить карту таким образом, что ключ и значение должны использовать один и тот же общий тип, поддерживая разные ключи, т.е. Вы не можете сделать Map<class<t>, IHandle<t>></t> </class<t> где каждый ключ будет отличаться от T но только позволяет значение, которое соответствует ключу.

Добавление дескриптора будет выглядеть так:

handles.put( handle.getMessageType(), handle );

Запрос этой карты с сообщением будет работать следующим образом:

IHandle<!--?--> handle = handles.get( message.getClass() ); 
Collection<subscriber> handleSubscribers = subscribers.get( handle );
</subscriber>

Обратите внимание: если вы хотите также получить подписчиков для суперклассов (например, если SpecialMessage extends Message и вы хотите получить те, которые подписались на Message), вам нужно будет подойти к иерархии классов.

Вот что вы сделали бы на один уровень:

handles.get( message.getClass().getSuperclass() );
for( Class<!--?--> interface : message.getClass().getInterfaces() ) {
 handles.get( message );
}

licensed under cc by-sa 3.0 with attribution.