I\'m writing a fluent API to configure and instantiate a series of \"message\" objects. I have a hierarchy of message types.
To be able to access method of subclasse
This is not a solution for your original problem. It is only an attempt to capture your actual intention, and sketch an approach where where the original problem does not appear. (I like generics - but class names like CommandMessage make me shudder...)
I know that this is structurally rather different from what you originally asked about, and you might have omitted some details in the question that narrow down the range of possible answers so that the following is no longer applicable.
But if I understood your intention correctly, you could consider letting the subtypes be handled by the fluent calls.
The idea here is that you initially can only create a simple Message:
Message m0 = Message.newMessage();
Message m1 = m0.withID("id");
On this message, you can call the withID method - that's the only method that all messages have in common. The withID method returns a Message in this case.
Until now, the message is neither a CommandMessage nor any other specialized form. However, when you call the withCommand method, you obviously want to construct a CommandMessage - so you now simply return a CommandMessage:
CommandMessage m2 = m1.withCommand("command");
Similarly, when you call the withParameter method, you receive a CommandWithParamsMessage:
CommandWithParamsMessage m3 = m2.withParameter("name", "value");
This idea is roughly (!) inspired by a blog entry, which is in German, but the code nicely shows how this concept may be used to construct type-safe "Select-From-Where" queries.
Here, the approach is sketched, roughly adapted for your use-case. Of course, there are some details where the implementation will depend on how this is actually going to be used - but I hope that the idea becomes clear.
import java.util.HashMap;
import java.util.Map;
public class FluentTest
{
public static void main(String[] args)
{
CommandWithParamsMessage msg = Message.newMessage().
withID("do").
withCommand("doAction").
withParameter("arg", "value");
Message m0 = Message.newMessage();
Message m1 = m0.withID("id");
CommandMessage m2 = m1.withCommand("command");
CommandWithParamsMessage m3 = m2.withParameter("name", "value");
CommandWithParamsMessage m4 = m3.withCommand("otherCommand");
CommandWithParamsMessage m5 = m4.withID("otherID");
}
}
class Message
{
protected String id;
protected Map contents;
static Message newMessage()
{
return new Message();
}
private Message()
{
contents = new HashMap<>();
}
protected Message(Map contents)
{
this.contents = contents;
}
public Message withID(String id)
{
this.id = id;
return this;
}
public CommandMessage withCommand(String command)
{
Map newContents = new HashMap(contents);
newContents.put("command", command);
return new CommandMessage(newContents);
}
}
class CommandMessage extends Message
{
protected CommandMessage(Map contents)
{
super(contents);
}
@Override
public CommandMessage withID(String id)
{
this.id = id;
return this;
}
public CommandWithParamsMessage withParameter(String paramName, String paramValue)
{
Map newContents = new HashMap(contents);
newContents.put(paramName, paramValue);
return new CommandWithParamsMessage(newContents);
}
}
class CommandWithParamsMessage extends CommandMessage
{
protected CommandWithParamsMessage(Map contents)
{
super(contents);
}
@Override
public CommandWithParamsMessage withID(String id)
{
this.id = id;
return this;
}
@Override
public CommandWithParamsMessage withCommand(String command)
{
this.contents.put("command", command);
return this;
}
}