代码之家  ›  专栏  ›  技术社区  ›  Simon

Java中的虚拟方法问题

  •  3
  • Simon  · 技术社区  · 14 年前

    简单地说:我想用下面的代码来打印“Sub”:

    Element e = new SubElement();
    print(e);
    ... 
    
    private static void print(Element e) {
        System.out.println("e");
    }
    
    private static void print(SubElement e) {
        System.out.println("sub");
    }
    

    我不想更改打印(元素E)。所以什么都不喜欢

    private static void print(Element e) {
        if (e instanceof SubElement) {
            print((SubElement) e);
        } else {
            System.out.println("e");
        }
    }
    

    我想做的是

    print(e.getClass().cast(e));
    

    自动将其转换为真正的子类并强制系统输入print(子元素e)。这有可能吗?

    5 回复  |  直到 14 年前
        1
  •  1
  •   helios    14 年前

    对。你可以使用 Visitor pattern . 但是,它适用于稳定的定义良好的层次结构,因为您必须定义的访问者接口需要为每种类型使用一个方法。

    interface ElementVisitor {
       visit(Element e);
       visit(SubElement se);
    }
    
    class ElementerPrinter implements ElementVisitor {
       visit(Element e) { System.out.println("e"); }
       visit(SubElement e) { System.out.println("sub"); }
    }
    
    class Element {
      // here's the trick, Element knows that this is Element
      // and childs have to implement it!
      // if the parent-most class is an interface it force you to implement!
      accept(ElementVisitor visitor) { visitor.visit(this); } 
    }
    
    class SubElement {
      // here's the trick, Element knows that this is SubElement
      accept(ElementVisitor visitor) { visitor.visit(this); }
    }
    
        2
  •  7
  •   krock    14 年前

    运行的重载方法是在编译时选择的,因此选择的是元素版本而不是子元素版本。更合乎逻辑的是让元素或子类包含应该打印的数据。

    class Element {
    
        public String getName() {
            return "e";
        }
    }
    
    class SubElement extends Element {
        public String getName() {
            return "sub";
        }
    }
    

    然后在打印方法中:

    private static void print(Element e) {
        System.out.println(e.getName());
    }
    

    这是否有意义将取决于 Element 类实际上是和打印数据所代表的内容。

        3
  •  1
  •   Sean Owen    14 年前

    print() 需要成为的实例方法 Element ,真的。你试图用一种很难的方式来模仿多态性。如果你想这样做,你就不能真正避免 if 映射自的语句 Class 功能对象。何苦?

        4
  •  0
  •   matt burns    14 年前

    您是否能够将行为差异推送到元素类中?

    Element e = new SubElement();
    print(e);
    ... 
    
    private static void print(Element e) {
        System.out.println(e.getMessageToPrint());
    }
    
    // no longer needed
    //
    //private static void print(SubElement e) {
    //    System.out.println("sub");
    //}
    

    这种方式, SubElement 可以覆盖 getMessageToPrint() 方法。

    或者更好:

    Element e = new SubElement();
    e.print();
    
        5
  •  0
  •   Sean Patrick Floyd    14 年前

    我会选择不同的方法。要么

    1. 按照其他人的建议使用多态性,扩展元素以添加print()方法(可以被子类覆盖),或者
    2. 定义一个助手接口,并使用策略和工厂模式的组合:

    基类

     public class Element{}
    

    派生类

     public class SubElement extends Element{}
    

    打印元素的帮助程序接口

    public interface PrintHelper{
        void print(Element element);
    }
    

    获取给定元素的最佳printHelper的工厂

    public class PrintHelperFactory{
    
        private final Map<Class<? extends Element>, PrintHelper> registeredHelpers =
            new HashMap<Class<? extends Element>, PrintHelper>();
    
        // Register a PrintHelper for a given Element class.
        public void registerHelper(final Class<? extends Element> clazz,
          final PrintHelper helper){
            this.registeredHelpers.put(clazz, helper);
        }
    
        // Get the most specific PrintHelper for a given Element.
        public PrintHelper getHelperForElement(final Element element){
            Class<? extends Element> clazz = element.getClass();
            while(!Object.class.equals(clazz)){
                if(this.registeredHelpers.containsKey(clazz)){
                    return this.registeredHelpers.get(clazz);
                }
                clazz = (Class<? extends Element>) clazz.getSuperclass();
            }
            return null;
        }
    
    }
    

    客户端测试类,运行为Java应用程序

    public class Main{
    
        public static void main(final String[] args){
    
            final PrintHelperFactory factory = new PrintHelperFactory();
            factory.registerHelper(Element.class, new PrintHelper(){
                @Override
                public void print(final Element element){
                    System.out.println("Element");
                }
            });
            factory.registerHelper(SubElement.class, new PrintHelper(){
                @Override
                public void print(final Element element){
                    System.out.println("Sub Element");
                }
            });
    
            // test it with an Element  
            final Element elem = new Element();
            factory.getHelperForElement(elem).print(elem);
    
            // test it with a sub class
            final Element sub = new SubElement();
            factory.getHelperForElement(sub).print(sub);
    
        }
    
    }
    

    产量

    Element
    Sub Element