Object Oriented Programming...function despatch

Age-old problem: you have two methods whose signatures only differ in one argument, and in one method the type of that argument is a super-class of the type of the same argument in the other method.

e.g.


public void method1( String a ) {} // 1
public void method1( MyExtendedString a ) {} // 2
}
...
class MyExtendedString extends String
{}

If you invoke:


MyExtendedString sub = new MyExtendedString( "blah" );
String parent = sub;

method1( parent ); // A
method1( sub ); // B
method1( (String) sub ); // C

you have the joy of not being entirely sure which method is going to get called. Obviously, A will be executed by 1, and B will be executed by 2.

The way it works is that C will then be executed by 1 (nb: if you like, I’ve got code that will demonstrate this happening).

My implementation of method 1 above checks if the argument is an instance of the subclass, and if so casts it to the subclass, and calls the same method again which, I would expect to cause method 2 instead to be invoked on the subclass - but it doesn’t, it just calls itself again (JDK 1.4.2).

Is this right? You can cast a sub class to a super to change the function that’s despatched, but you can’t do vice versa? But if you pass in a valid sub class it WILL be despatched to the subclass-specific function? I regularly (if infrequently) get problems with differences between how I remember java’s function despatcher and how it actually is, but I thought I had it pretty well sussed by now :(.

Since when can you extend String? It’s final.

edit BTW… it works as expected for me if I extend an extendable class. e.g. the cast makes the method with the more specific type be called.


public class Test
{
      public static void main(String [] args)
      {
            Test x = new Test();
            A a = new A();
            B b = new B();
            
            x.m1( a );
            x.m1( b );
            x.m1( (A)b);
      }
      
      void m1( A s )
      {
            if( s instanceof B )
            {
                  System.out.println("casting...");
                  m1( (B) s );
            }
            else
                  System.out.println("m1 - A");
      }
      
      void m1( B s)
      {
            System.out.println("m1 -- B:");
      }
}
class A
{
}
class B extends A
{
}

From the java spec on overloading:

[quote]When a method is invoked (§15.12), the number of actual arguments and the compile-time types of the arguments are used, at compile time, to determine the signature of the method that will be invoked (§15.12.2).
[/quote]
Because the compile time type is used, there’s no uncertainty as to which version of the overloaded method will be invoked.

As overloaded methods may have different return types it’s a compile-time issue deciding what method gets called.

You need a language with dynamic method dispatch (Lisp, Dylan just to name two) to achieve what you want - I think.

The usual workaround is double dispatch or using the visitor pattern. That is, instead of

class Base {}
class Sub extends Base {}
class Util {
  void m(Base b) ...
  void m(Sub b) ...
}

use

class Base {
  void accept() ...
}
class Sub extends Base {
  void accept() ...
}
class Util {
  void m(Base b) { b.accept(); }
}

This will correctly dispatch you message.