Hello, thought I'd ask before getting too deep into code to make sure I understand this properly.
As described in the Gamma et. al. book the Visitor Pattern looks something like this:

interface Visitor{
    public void visitX(X x);
    public void visitY(Y y);
    public void visitZ(Z z);
}
interface Visited {
    public void visit(Visitor v);
}
class X implements Visited{
    public void visit(Visitor v){
        v.visitX(this);
    }
}
class Y implements Visited{
    public void visit(Visitor v){
        v.visitY(this);
    }
}
class Z implements Visited{
    public void visit(Visitor v){
        v.visitZ(this);
    }
}

//SOMEWHERE ELSE A CLASS
public void doAlgorithm(Visitor visitor){
    for(Visited v : datastructure){
        v.visit(visitor);
    }
}

The book mentions doing the same with overloading so that would become:

interface Visitor {
    public void visit(X x)
    public void visit(Y y)
    public void visit(Z z)
}
interface Visited {
    public void visit(Visitor v);
}
class X implements Visited{
    public void visit(Visitor v){
        v.visit(this);
    }
}
class Y implements Visited{
    public void visit(Visitor v){
        v.visit(this);
    }
}
class Z implements Visited{
    public void visit(Visitor v){
        v.visit(this);
    }
}

//SOMEWHERE ELSE IN A CLASS
public void algorithm(Visitor visitor){
    for(Visited v : datastructure){
        v.visit(visitor);
    }
}

I may be very off on this but this leads me to think that this would work as well:

interface Visitor{
    public void visit(X x);
    public void visit(Y y);
    public void visit(Z z);
    public void visit(Visited v);//as a default
}
abstract class Visited{
    public final void visit(Visitor v){
        v.visit(this);
    }
}
class X extends Visited {}
class Y extends Visited {}
class Z extends Visited {}
//SOMEWHERE IN A CLASS
public void algorithm(Visitor visitor){
    for(Visited v : datastructure){
        v.visit(visitor);
    }
}

But as I understand it the method used is decided at compile time, so this would always call visit(Visitor) , the reason I include this method in the first place is, I want to make the application extensible with the possibility of extending the amount of classes that exted Visited with a user defined class I don't want to break the existing visitors. Would the proper implementation of Visited.visit(Visitor) be:

public void visit(Visitor v){
    try{
        Method m = v.getClass().getMethod("visit", getClass());
        m.invoke(v,this);
    } catch(Exception e){
        v.visit(this);
    }
}

Which would actually render the Visitor interface moot, as it inspects the class itself. Can someone clarify this please?

Recommended Answers

All 5 Replies

But as I understand it the method used is decided at compile time,

No, instance methods are bound at runtime based on the actual class of the object upon which the call is being made. (The method is determined at compile time for static methods). So you can simply define a single method for the interface and each class that implements that interface will provide its own implementation, and the right one will always be called. This is a major benefit of the OO paradigm.
Using reflection is an advanced technique that is rarely if ever needed for ordinary programming like this.

No, instance methods are bound at runtime based on the actual class of the object upon which the call is being made. (The method is determined at compile time for static methods). So you can simply define a single method for the interface and each class that implements that interface will provide its own implementation, and the right one will always be called. This is a major benefit of the OO paradigm.
Using reflection is an advanced technique that is rarely if ever needed for ordinary programming like this.

It appears that I made a mistake in the way I explained, I understand this just fine, the compile time determination I was reffering to was not based on the implicit object but the explicit parameters. This time i have compileable and runnable code to help explain why reflection is probably the best way to go in this situation:

import java.lang.reflect.Method;
class SuperClass{}
class X extends SuperClass{}
class Y extends SuperClass{}
class Z extends SuperClass{}
class W extends SuperClass{}
public class TestOverride
{
    void print(SuperClass s){
        System.out.println("print superclass");
    }
    void print(X s){
        System.out.println("print X");
    }
    void print(Y s){
        System.out.println("print Y");
    }
    void print(Z s){
        System.out.println("print Z");
    }
    void printReflective(SuperClass s){
        try{
            Method m = getClass().getDeclaredMethod("print", s.getClass());
            m.invoke(this,s);
        } catch(Exception e){
            print(s);
        }
    }
    public static void main(String[] args){
        TestOverride test = new TestOverride();
        X asX = new X();
        Y asY = new Y();
        Z asZ = new Z();
        W asW = new W();
        SuperClass asSuper = asX;

        System.out.println("printing subclasses: ");
        test.print(asX);
        test.print(asY);
        test.print(asZ);
        test.print(asW);

        System.out.println("printing subclasses as superclass:");
        test.print(asSuper);
        asSuper=asY;
        test.print(asSuper);
        asSuper=asZ;
        test.print(asSuper);
        asSuper=asW;
        test.print(asSuper);

        System.out.println("printing subclasses reflective: ");
        test.printReflective(asX);
        test.printReflective(asY);
        test.printReflective(asZ);
        test.printReflective(asW);

        System.out.println("printing subclasses as superclass reflective:");
        asSuper=asX;
        test.printReflective(asSuper);
        asSuper=asY;
        test.printReflective(asSuper);
        asSuper=asZ;
        test.printReflective(asSuper);
        asSuper=asW;
        test.printReflective(asSuper);


    }
}

Which produces this output:

printing subclasses: 
print X
print Y
print Z
print superclass
printing subclasses as superclass:
print superclass
print superclass
print superclass
print superclass
printing subclasses reflective: 
print X
print Y
print Z
print superclass
printing subclasses as superclass reflective:
print X
print Y
print Z
print superclass

When the parameter is generalized to superclass the print(SuperClass) method is called regardless of the actual type. The reflective method behaves "correctly" in this instance where the non-reflective code does not.

OK.
So if a method (eg print) is different for X, Y, and Z surely it should be implemented in or delegated to those classes (at least for the parts that differ from one class to another). In which case you can use Java's normal binding to call the right one...

   void print(SuperClass s){
     s.print(); // calls the print method for whatever subclass os SuperClass was passed
   }

The idea behind the visitor pattern is to make the classes not aware of what is being done to them. the Visitor has a method for each type of object like above. The classes that are being manipulated have don't know what is to be done with them. In the OP the individual classes only have a method that accepts a visitor and passes themselves to the visitor. In the link the java implementation overrides the visit method in each sub-object so the compiler can distinguish between them.

I'm looking for a way to dispach it through the superclass so that the children don't have to do anything and it appears that the reflective version is the only way. I don't want someone who implements a subclass to have to write a method that does the same exact thing every time, I would rather the superclass take care of it so the future use doesn't have to know that their class can be visited.

Yes, if you really want to implement the full classic Visitor in Java then you need reflection. Personally I would question its relevance in Java, and I hate the way it violates basic encapsulation/localisation criteria. If "someone who implements a subclass to have to write a method that does the same exact thing every time" then that method should be implemented in the superclass anyway. Only the specific elements that vary from subclass to subclass need to be implemented for each subclass, in each subclass.
However, this is a very theoretical kind or argument that I'm not going to waste your time with. Yes, go with reflection. ;)

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.