【问题标题】:Sending in an object of type Object instead of String - Polymorphism发送 Object 类型的对象而不是 String - 多态性
【发布时间】:2011-02-27 16:49:33
【问题描述】:

我有一个看起来像这样的现有方法:

  public void parseMessage(String message){
    ...
    ...
    ...
  }

并通过如下所示调用方法来调用它

 String message;
 parseMessage(message);

我需要对其进行修改以使其处理新类型的消息。从 parseMessage 方法调用的新型消息的解析器在解析消息之前首先需要一些属性。我正在考虑做的是将消息作为一个看起来像这样的对象传递

public class MessageObject{
  private String message;
  private String namespace;
  private String xmlVersion;
}

然后我可以将现有方法称为

 Object messageObject;
 parseMessage(messageObject);

然后我可以通过将其转换为 (MessageObject)messageObject 来在另一端使用它。

这是正确的方法还是有更好的方法。进行上述操作有什么危险吗?

ps。我必须使用上面的转换方法,因为我使用 JDK1.4

谢谢

更新

我无法修改 parseMessage 方法。它内部有一个调用,它为每个相关的解析器调用 parse() 方法。

 public void parseMessage(String message){
    ...
    ...
    parser.parse(message)
  }

上面显示的解析器引用是一个实现接口“解析器”的对象。我介绍的新解析器遵循这个结构,它还实现了“解析器”接口。唯一的修改(即转换为 MessageObject)在新的解析器中。

我无法更改现有的 parseMethod,因为这将需要更改所有实现“解析器”接口的现有解析器。我想避免根据消息类型调用特定的解析器。

如果我使用我建议的方法,现有解析器仍会收到字符串消息,而新解析器将收到字符串,但需要先将其转换回 MessageObject。

编辑

我不得不根据 Sergey 的 cmets 对此进行测试。

界面

    package com;
    public interface Parser{
        public void parse(String message);
    }


    package com;

消息解析器

    public class MessageAParser implements Parser{
        public void parse(String message){
            System.out.println("Parsing A");
        }
    }

MessageB 解析器

    package com;

    public class MessageAParser implements Parser{
        public void parse(String message){
            System.out.println("Parsing A");
        }
    }

MessageC 解析器(这需要一个对象)

    package com;
    public class MessageCParser implements Parser{
        public void parse(Object message){
            MessageObject mobject = (MessageObject)message; 
            System.out.println("Parsing C" + mobject.getMessage());
        }

        public void parse(String m){}
    }

调用相关解析器的解析器管理器。

    package com;

    import java.util.HashMap;

    public class ParserManager{

        public ParserManager() {
            prepare();
        }

        HashMap parsers = new HashMap();


        public void prepare(){

            parsers.put("A",new MessageAParser());
            parsers.put("B",new MessageBParser());
            parsers.put("C",new MessageCParser());
        }

        public void parseMessage(String msgType, String message){
            ((Parser)parsers.get(msgType)).parse(message);
        }
    }


    package com;

控制器。

    public class ControlClass{

        public static void main(String[] args){

            ParserManager pManager = new ParserManager();

            //Parse A
            pManager.parseMessage("A","ATEXT TO PARSE");

            //Parse B
            pManager.parseMessage("B","BTEXT TO PARSE");

            //Parse C
            Object mobject = new MessageObject();
            pManager.parseMessage("C",(String)mobject);
        }
    }

当我运行上面的 Controller 类时,它输出前两条消息的文本,但第三条消息失败。

Parsing A
Parsing B
java.lang.ClassCastException
    at com.ControlClass.main(ControlClass.java:17)
Exception in thread "main" 

【问题讨论】:

    标签: java interface casting polymorphism


    【解决方案1】:

    MessageObject 类不是 String 子类,所以不能传递它来代替 String。而且你不能继承 String 因为它被声明为 final 。因此,您无法将 MessageObject(无论是什么)传递给现有的 parseMessage() 函数。

    我无法更改现有的 parseMethod 因为这需要改变所有 实现的现有解析器 “解析器”接口。我想要 避免调用特定的解析器 取决于消息类型。

    Parser.parse() 的签名到底是什么?如果是parse(String message),那么除了字符串之外,你不可能传递任何东西。

    但是,如果这是您不想修改现有 parseMessage() 的唯一原因,那么有一个解决方法:

    public void parseMessage(Object message) { // changed to Object
      ...
      ...
      if (message instanceof String)
        parser.parse((String) message);
      else {
        if (message instanceof MessageObject) {
          if (!(parser instanceof MessageObjectParser)) {
            throw new IllegalArgumentException(
              "A MessageObject is passed, but not supported by the parser");
          }
          ((MessageObjectParser) parser).parse((MessageObject) message);
        } else {
          throw new IllegalArgumentException(
            "Messages of type " + parser.getClass() + " aren't supported");
        }
      }
    }
    

    这有点难看,但可能会起作用。现在您只需让新的解析器实现新的 MessageObjectParser 接口,该接口应该扩展旧的 Parser。

    【讨论】:

    • 有几个解析器。每个实现“Parser”接口的解析器都有一个方法 parse(String message)。我添加的新解析器也实现了“解析器”接口,它使用签名解析(对象消息)。你确定这行不通吗?我认为既然 String 是 Object 的子类,它应该可以工作。
    • @ziggy:您可以使用该签名实现解析器,但您还必须实现parse(String message),否则编译器不会接受它作为有效的接口实现。所以你最终会有两个重载的方法。但是如果 parseMessage() 方法通过 Parser 接口访问解析器,它无论如何都不会看到你的重载方法(接受对象)。您必须将其显式转换为您的实现类(这是一个坏主意),或者像我在示例中显示的那样实现另一个接口。
    • @ziggy,String 是 Object 的子类这一事实意味着您可以在需要 Object 的任何地方使用 String,而不是相反。但是如果 Parser 接口声明了parse(String message),那么它需要一个字符串,而不是一个对象。而且你不能用不同的签名来实现它。
    • 我认为你是对的。我尝试了一个简单的示例,但它因需要对象的解析器的类转换异常而失败。请参阅 OP 中的第二个编辑。
    【解决方案2】:

    您可以重载 parseMessage,因此它有两种形式:一种采用 String 参数,另一种采用 MessageObject 参数。

    【讨论】:

      【解决方案3】:

      我认为这里有几个更清洁的解决方案。首先是你可以扩展实现 parseMessage 的类并添加一个额外的方法。

      public void parseMessage(MessageObject messageObject) {
        // Additional stuff here
        ...
      
        // Call through to original
        parseMessage(messageObject.message);
      }
      

      或者,您可以只装饰包含 parseMessage 的类。但是,我假设您可以修改包含 parseMessage 的类,因为您说无论如何您都想将它投射到那里。

      【讨论】:

        【解决方案4】:

        如果你要解析的东西类型越来越多,与其重载parse方法,不如引入一个接口:

        public interface Parseable {
            public String getMessage();
        }
        

        MessageObject 将实现 Parseable,您可以为 String 对象使用匿名适配器类:

        final String m = "theMessageToParse";
        parseMessage(new Parseable() {
            public String getMessage() {
                return m;
            }
        });
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-11-16
          • 1970-01-01
          • 2017-08-10
          • 2021-07-10
          • 1970-01-01
          • 1970-01-01
          • 2023-03-31
          • 2022-12-23
          相关资源
          最近更新 更多