Fluent API with inheritance and generics

后端 未结 5 788
谎友^
谎友^ 2020-12-09 02:13

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

5条回答
  •  长情又很酷
    2020-12-09 02:30

    Your code is fundamentally an unsafe use of Generics. For example, if I write a new class which extends message, say Threat, and has a new method doSomething(), and then I create a message parameterised by this new class and it creates an instance of Message, and then attempts to Cast it to its subclass. However, since it is an instance of Message, and not of Threat, an attempt to call this message will cause an Exception. Since it is not possible for Message to doSOmething().

    Further, its also unnecessary to use Generics here. Plain old inheritance will work fine. Since sub types can override methods by making their return types more specific, you can have:

    public abstract class Message {
    
        protected Message() {
    
        }
    
        public Message withID(String id) {
            return this;
        }
    }
    

    And then

    public class CommandMessage extends Message {
    
        protected CommandMessage() {
            super();
        }
    
        public static CommandMessage newMessage() {
            return new CommandMessage();
        }
    
        public CommandMessage withCommand(String command) {
            return this;
        }
    }
    

    This will work fine, on the understanding that you call your arguments in the right order:

    CommandWithParamsMessage.newMessage()
        .withID("do")
        .withCommand("doAction")
        .withParameter("arg", "value");
    

    will fail, but

    CommandWithParamsMessage.newMessage().withParameter("arg", "value")
    .withCommand("doAction").withID("do")
    

    Will succeed, since it only "up types", finally returning a "message" class. If you want it not to "uptype", then simply overwrite the inherited commands, and now you can call the methods in any order, since they are all return the original type.

    E.g.

    public class CommandWithParamsMessage extends
    CommandMessage {
    
        public static CommandWithParamsMessage newMessage() {
            return new CommandWithParamsMessage();
        }
    
        public CommandWithParamsMessage withParameter(String paramName,
            String paramValue) {
            contents.put(paramName, paramValue);
            return this;
        }
    
        @Override
        public CommandWithParamsMessage withCommand(String command){
            super.withCommand(command);
            return this;
       }
    
        @Override
        public CommandWithParamsMessage withID(String s){
            super.withID(s);
            return this;
        }
    }
    

    Now you will fluently return a CommandWithParamsMessage with either of the two fluent calls above.

    Does this solve your problem, or have I misunderstood your intent?

提交回复
热议问题