1

My starting point is the following:
- I have a method, transform, which I overloaded to behave differently depending on the type of arguments that are passed in (see transform(A a1, A a2) and transform(A a1, B b) in my example below)
- All these arguments implement the same interface, X

I would like to apply that transform method on various objects all implementing the X interface.

What I came up with was to implement transform(X x1, X x2), which checks for the instance of each object before applying the relevant variant of my transform.

Though it works, the code seems ugly and I am also concerned of the performance overhead for evaluating these various instanceof and casting. Is that transform the best I can do in Java or is there a more elegant and/or efficient way of achieving the same behavior?

Below is a trivial, working example printing out BA. I am looking for examples on how to improve that code. In my real code, I have naturally more implementations of 'transform' and none are trivial like below.

public class A implements X {
}

public class B implements X {
}

interface X {
}

public A transform(A a1, A a2) {
  System.out.print("A");
  return a2;
}

public A transform(A a1, B b) {
  System.out.print("B");
  return a1;
}

// Isn't there something better than the code below???
public X transform(X x1, X x2) {
  if ((x1 instanceof A) && (x2 instanceof A)) {
    return transform((A) x1, (A) x2);
  } else if ((x1 instanceof A) && (x2 instanceof B)) {
    return transform((A) x1, (B) x2);
  } else {
    throw new RuntimeException("Transform not implemented for "
            + x1.getClass() + "," + x2.getClass());
  }
}

@Test
public void trivial() {
  X x1 = new A();
  X x2 = new B();
  X result = transform(x1, x2);
  transform(x1, result);
}
double07
  • 2,565
  • 2
  • 22
  • 22
  • I also found the following post relevant: http://sites.google.com/site/steveyegge2/when-polymorphism-fails – double07 Jan 13 '11 at 04:13
  • 1
    Performance should be of no concern — unless you plan to run that section of code very, very often. Make it work first, optimize later. – Bombe Jan 13 '11 at 08:23

3 Answers3

5

Take a look at the Visitor pattern as a starting point.

If your hierarchy changes a lot, the visitor pattern spreads the changes throughout. In that case, also look at the acyclic visitor.

The code could look like this:

public interface X {
  void accept(XVisitor v);
}

public interface XVisitor { 
  void visit(A a);
  void visit(B b);
}

public class A implements X {
  public void accept(XVisitor v) {
    v.visit(this);
  }
}

public class B implements X {
  public void accept(XVisitor v) {
    v.visit(this);
  }
}

And then your algorithm goes into this class:

public class XTransformerVisitor implements XVisitor {
  private X result;
  private A first;
  public void visit(A a) {
    if (first == null) first = a;
    else result = a;
  }
  public void visit(B b) {
    if (first == null) throw new RuntimeException();
    result = first;
  }
  public X transform(X x1, X x2) {
    x1.accept(this);
    x2.accept(this);
    return result;
  }
}
Jordão
  • 55,340
  • 13
  • 112
  • 144
  • Thanks for the answer. My hierarchy won't change. I am more concerned about the readability of the code and possible performance impact of invoking instanceof and performing casting. After reading the example you pointed me to, I am still unclear whether the visitor pattern would be a better tradeoff in my case. Would it? – double07 Jan 13 '11 at 03:05
  • Maybe, that's a start... I'll post some code and you'll be the judge. – Jordão Jan 13 '11 at 03:10
  • Ok, I am sold. And thanks for the example: made it much more concrete. – double07 Jan 13 '11 at 03:28
3

The term that you're looking for is multiple dispatch, which is a generalization of virtual functions that are polymorphic in the types of multiple arguments. Most programming languages, including Java and C++, do not have support for multiple dispatch, and so require some sort of hackery to emulate it. One option is to have code like what you have above, and another would be to use something like this. One common solution is to use an idiom called the visitor pattern, which can help abstract away the complexity.

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
0

After some more research, I came across the concept of reflection. I think this is by far simpler than the visitor pattern, at least to solve the specific problem here.

My original code above can stay exactly the same and the annoying method transform(X x1, X x2) simply becomes:

public X transform(X x1, X x2) {
  Method m;
  try {
    m = getClass().getMethod("transform",
            new Class[]{x1.getClass(), x2.getClass()});
    return (X) m.invoke(this, new Object[]{x1, x2});
  } catch (Exception e) {
    throw new RuntimeException("Transform not implemented for "
        + x1.getClass() + "," + x2.getClass());
  }
}

Advantages:
- gets rid of the nested instanceof tests and casting I had in my original post
- no need for implementing an accept method that all operands must implement brought by the double-dispatch/visitor pattern approach

double07
  • 2,565
  • 2
  • 22
  • 22