Generics

matthew016
matthew016 used Ask the Experts™
on
Hi,

This is an uneasy question about Generics, so continue if you are very familiar with this concept:

I have a class Action defining an "execute" method:

public abstract class Action<C extends Client> {
    public abstract void execute(LinkedList<String> fields, C client);

This class Action has a whole set of subclasses :

* public class ActionValidateConnection extends Action<EndpointClient> {
      public void execute(LinkedList<String> fields, EndpointClient client);

* public class ActionEcho extends Action<EndpointClientAdmin> {
      public void execute(LinkedList<String> fields, EndpointClientAdmin client);

* etcetera

I use Generics above because the execute method will, as you may see above, either take an EndpointClient interface or an EndpointClientAdmin interface which are both extending Client interface. That's why the class has the following signature: public abstract class Action<C extends Client> { ...

In short, I have a client which recieves messages from the server, and the client is executing the appropriate action according to the message type. I have two types of actions, members actions and admin actions.

Then, I have a dictionary mapping => type of message/Action object :

protected static final Action<EndpointClient> ACTION_SEND_PRIVATE = new ActionSendPrivateMessage();
protected static final Action<EndpointClientAdmin> ACTION_SEND_TO_ALL = new ActionSendMessageToAll();
dictionary.put(Constants.MSG_SEND_PRIVATE, ACTION_SEND_PRIVATE);
dictionary.put(Constants.MSG_SEND_TO_ALL, ACTION_SEND_TO_ALL);

As you may see I want to put Action<EndpointClient> for members messages in the dictionary and Action<EndpointClientAdmin> for admin messages. Per consequent, the dictionary needs to be :
protected static HashMap<String, Action<? extends Client>> dictionary =
    new HashMap<String, Action<? extends Client>>();
And to get an Action from the dictionary :
public Action<? extends Client> getAction(String command) { ... }

Now I have one error in the class which recieves the messages from the server : MessageProcessor
I get the type of message:

public MessageProcessor(String data, EndpointClient client) {
    String[] fields = data.split(Constants.FIELD_DELIMITER);
    String command = fields[0];

Then get the Action from the dictionary:

    Action<? extends Client> action = directory.getAction(command);

And finally execute the Action :

    action.execute(listFields, client);

Error on the line above :

The method execute(LinkedList<String>, capture#3-of ? extends Client) in the type Action<capture#3-of ? extends Client> is not applicable for the arguments (LinkedList<String>, EndpointClient)

I understand the problem that occurs, but I can't find a solution for it. If I change something, error occur other place, etc.
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
If I understood the problem correctly (it's a bit hard without full source code), I believe you should explicitly cast the response from the map to ActionValidateConnection , like so:
ActionValidateConnection action = (ActionValidateConnection)directory.getAction(command);

The reason for this is that at this point the action you receive MUST be ActionValidateConnection. If it's not, for example, if it were ActionEcho, you wouldn't have been able to call its execute with "client" whose type is EndpointClient, since the method implementation in ActionEcho doesn't receive EndpointClient, it receives EndpointClientAdmin.

If at this point in your code you KNOW that the value returned from the map is one that can receive an EndpointClient intstance in its action method, then you know more than the code, you know that you actually get an ActionValidateConnection instance, and should cast it accordingly (of course, you can always use instanceof to make sure).
After you cast it to ActionValidateConnection, you should have no problem calling action with "client"

your code should look something like this,

the whole problem is Client is not generic, so we would need to wrap it as a generic.

,
your client code now will look like

ClientContainer<EndpointClient> c = null ; //get the instance
            Action<EndpointClient> action = null ;// directory.getAction(command);
          action.execute(new LinkedList<String>(), c);
interface Client{
	
}

abstract class EndpointClientAdmin implements Client{
	
}
abstract class EndpointClient implements Client{
	
}

abstract class ClientContainer<a extends Client>{
	abstract a getClient();
}

abstract class Action<C extends Client> {
    public abstract void execute(LinkedList<String> fields, ClientContainer<C> client);
}

 class ActionValidateConnection extends Action<EndpointClient> {
    public void execute(LinkedList<String> fields, EndpointClient client){
    	
    }

	@Override
	public void execute(LinkedList<String> fields,
			ClientContainer<EndpointClient> client) {
	}
	
}
 
 class ActionEcho extends Action<EndpointClientAdmin> {
    public void execute(LinkedList<String> fields, EndpointClientAdmin client){
    	
    }

	@Override
	public void execute(LinkedList<String> fields,
			ClientContainer<EndpointClientAdmin> client) {
	}
}

Open in new window

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial