简体   繁体   中英

question about virtual methods in java

Put simply: I want the following code to print "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");
}

and i dont want to change print(Element e). so nothing like

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

what i would like to do is

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

to automatically cast it to the real subclass and force the system to enter print(SubElement e). is this somehow possible?

The overloaded method that is run is chosen at compile time so that is why the Element version is chosen rather than the SubElement version. What would seem more logical would be to have the Element or subclass contain the data that should be printed.

class Element {

    public String getName() {
        return "e";
    }
}

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

and then in the print method:

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

Whether this is going to make sense will depend on what the Element class actually is and what the printed data represents.

Yes. You can use Visitor pattern . However it's suitable for stablished well-defined hierarchies, because the Visitor interface you have to define needs a method for each type.

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); }
}

print() needs to become an instance method of Element , really. You're trying to imitate polymorphism in a hard way otherwise. If you wish to do that, you can't really avoid some series of if statements of mappings from Class to function objects. Why bother?

Are you able to push the difference of behaviour into the element classes?

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");
//}

This way, SubElement can override the getMessageToPrint() method.

Or better still:

Element e = new SubElement();
e.print();

I would choose a different approach. Either

  1. use polymorphism as suggested by others, extend Element to add a print() method (which can be overwritten by sub classes) or
  2. define a helper interface and use a combination of strategy and factory pattern:

Base Class

 public class Element{}

Derived Class

 public class SubElement extends Element{}

Helper Interface to print elements

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

Factory to get the best PrintHelper for a given element

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;
    }

}

Client test class, run as Java Application

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);

    }

}

Output

Element
Sub Element

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM