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 subclasses when using the fluent API, I used generics to parametrize the subclasses and make all fluent methods (that start with "with") return the generic type. Note that I omitted most of the body of the fluent method; a lot of configuration goes on in them.
public abstract class Message<T extends Message<T>> {
protected Message() {
}
public T withID(String id) {
return (T) this;
}
}
The concrete subclasses redefine the generic type similarly.
public class CommandMessage<T extends CommandMessage<T>> extends Message<CommandMessage<T>> {
protected CommandMessage() {
super();
}
public static CommandMessage newMessage() {
return new CommandMessage();
}
public T withCommand(String command) {
return (T) this;
}
}
public class CommandWithParamsMessage extends
CommandMessage<CommandWithParamsMessage> {
public static CommandWithParamsMessage newMessage() {
return new CommandWithParamsMessage();
}
public CommandWithParamsMessage withParameter(String paramName,
String paramValue) {
contents.put(paramName, paramValue);
return this;
}
}
This code works, i.e. I can instantiate any of the classes and use all fluent methods:
CommandWithParamsMessage msg = CommandWithParamsMessage.newMessage()
.withID("do")
.withCommand("doAction")
.withParameter("arg", "value");
Calling the fluent methods in any order is a major goal here.
However, the compiler warns that all return (T) this
are unsafe.
Type safety: Unchecked cast from Message to T
I'm unsure how I could reorganize the hierarchy to make this code truly safe. Even though it works, the use of generics in this fashion feels really convoluted. Especially, I'm not able to foresee situations where runtime exceptions will happen if I just ignore the warnings. There will be new message types, so I need to keep the code extensible. If the solution is to avoid inheritance altogether I would also like to obtain suggestion of alternatives.
There are other questions here on SO that address a similar issue. They point to a solution where all intermediate classes are abstract and declare a method like protected abstract self()
. Still, in the end it's not safe.
Message
is of typeT
? – ForliniT extends Message<T>
. – Campballpublic Message<T> withID(String id) {return this;}
instead ofpublic T withID(String id) {return (T) this;}
. Do same forCommandMessage
as well. – PartitionThe method withCommand(String) is undefined for the type Message<CommandMessage<CommandWithParamsMessage>>
– Transportation