0

I want to avoid redefining accept() in every class extending Containers, so I defined it in containers. Java won't compile it except if I create a function visit(Container cont) in my visitor. I thought that by doing this, I would force the compilation but the visit(Container cont) would never be used ... because Java takes the lowest class, but it didn't.

Result : I got an infinite recursive loop using every time visit(Container cont)...

Can you explain me why and how to fix it ( without defining accept in every container and making FinderPackBuilder an interface ).

Thank you !

EDIT : The result of element.getClass() is never a Container, I tested it.

public abstract class FinderPackBuilderVisitor {

abstract public Document visit (Module module);
abstract public Document visit (Dashboard dashboard);
abstract public Document visit (Section section);
abstract public Document visit (Metric metric);
// The last visit method is here to ensure that all containers can use visit. It will never be used since Container is not Instantiable.
// Another alternative would be to make this an interface and delete this method but we would have to dupliacte code in every instantiable class.
Document visit (Container element){
    System.out.println(element.getClass());
    System.out.println("This function shouldn't be taken");
    return visit(element);
}



public abstract class Container<T extends Container> {

protected String name;
protected ArrayList<T> children;

public Container(String n, ArrayList<? extends T> c){

    name = n;
    children = new ArrayList<T>();

    for (T child : c){
        children.add((T)child.getCopy());
    }

}


Document accept(FinderPackBuilderVisitor visitor){

    for (T child : children){
        child.accept(visitor);
    }
    System.out.println(this.getClass());
    return visitor.visit(this);
}

abstract Container<T> getCopy();

}

The Answer
  • 279
  • 1
  • 4
  • 11

1 Answers1

1

You have to add a visit method accepting type Container to your Visitor, cause in your accept method in Container class you pass the static type Container to the visit method.

Thats the only way to be shure that there is always a visit method matching the dynamic type of the container who calls the visit method.

Imagine you don't have a visit(Container element) method in your visitor and you create new class deriving from Container. Now your code could not execute anymore cause there is no matching visit method in your visitor.

You could add an abstract method visit(Container element) in your vistor and remove the other abstract definitions of the visit method. In your concrete visitor you could implement the visit(Container element) method and check the dynamic type of the element. Based on this type you could call other methods doing the expected work.

MarioG
  • 136
  • 1
  • 6
  • Thank you for the answer. But I didn't understand clearly your solutions. Also I don't understand why even if the object is a dashboard it doesn't take the visit(Dashboard dash) method but the visit(Container cont) method ? – The Answer Aug 05 '15 at 10:57
  • 1
    Your welcome. When calling overloaded methods, Java uses the the compile time (static) type of the parameters to choose which of the overloaded methods to call. Thats why the other methods are never called. When using your code you then get stuck in an endless recursion cause the visit method always calls itself. See this answer for more details: [link](http://stackoverflow.com/questions/1572322/overloaded-method-selection-based-on-the-parameters-real-type). – MarioG Aug 05 '15 at 11:21
  • Then how can I make Java use the real type and not the static type ? – The Answer Aug 05 '15 at 11:26
  • 1) Derive an own Visitor for each Container type. E.G. a `DashboardVisitor` and override the visitor method there. When having a Dashboard you have to pass this derived Visitor to the accept method. 2) You could check the type of the given element in the visitor method using the `instanceof` operator and call further methods based on the dynamic type. 3) You could check the type of the container in the accept method using the `instanceof` operator and cast the container to this type when calling the visit function. Then the overridden method with the type matching the dynamic type is called. – MarioG Aug 05 '15 at 11:41