So my last entry on static vs. dynamic dispatch and how it interacts with method overloading got caught up in some weird rules relating to the automatic promotion of primitive types. Now I have concocted an example that runs, unmodfied, in both Java and Groovy. Caution: This is a code heavy post.
public class BabyColors {
String color(Boy b) { return "Blue"; }
String color(Girl g) { return "Pink"; }
String color(Baby a) { return "Green"; }
String whatColor(Baby a) { return color(a); }
public static void testDirect() {
BabyColors bc = new BabyColors();
Baby a = new Baby();
Baby bb = new Boy();
Baby bg = new Girl();
Boy b = new Boy();
Girl g = new Girl();
bc.color(a);
bc.color(bb);
bc.color(bg);
bc.color(b);
bc.color(g);
}
public static void testIndirect(int iterations) {
BabyColors bc = new BabyColors();
Baby a = new Baby();
Baby bb = new Boy();
Baby bg = new Girl();
Boy b = new Boy();
Girl g = new Girl();
bc.whatColor(a);
bc.whatColor(bb);
bc.whatColor(bg);
bc.whatColor(b);
bc.whatColor(g);
}
public static void main(String[] s) {
BabyColors bc = new BabyColors();
Baby a = new Baby();
Baby bb = new Boy();
Baby bg = new Girl();
Boy b = new Boy();
Girl g = new Girl();
System.out.println("Indirect -------");
System.out.println("unknown - " + bc.whatColor(a));
System.out.println("baby boy - " + bc.whatColor(bb));
System.out.println("baby girl - " + bc.whatColor(bg));
System.out.println(" boy - " + bc.whatColor(b));
System.out.println(" girl - " + bc.whatColor(g));
System.out.println("Direct ---------");
System.out.println("unknown - " + bc.color(a));
System.out.println("baby boy - " + bc.color(bb));
System.out.println("baby girl - " + bc.color(bg));
System.out.println(" boy - " + bc.color(b));
System.out.println(" girl - " + bc.color(g));
}
}
class Baby {}
class Boy extends Baby {}
class Girl extends Baby {}
Seems simple enough, rather than using virtual methods to store the information as to what color a baby blanket you would buy for a co-workers new baby. If it's a buy, you get blue. If it's a girl, you get pink. Not sure? Green. The Baby color class keeps this information because, after all, babies don't know what color blanket they should have. You throw an old beach towel on some of them when they sleep and they will be happy (not that I've done this...).
Ok, back to the post. What will Java output? Think about this before you scroll down too far. Java looks a the types it can prove at compile time and fixes the method it will call based on the type the variable is claiming to be, not the type it actually is at run time. So when we filter all of the calls to color through an intermediary that only accepts Baby objects it will treat them as thought that's all they can ever be, Babys. Even if the compiler knows at the time that the call is being processed that the type must be a boy or a girl, it forgets all of that information when it goes into the whichColor method. Now when the color method is called directly, in the two instances where the type of the object is declared to be a Boy or a Girl the gender specific variant is called.
Now there is the dynamically typed method dispatch (or multiple dispatch, or multimethods) which on current generation JVMs is about a thousand times slower. Java itself doesn't support this invocation style, but Groovy supports this style as the only style of method invocation. At runtime when you invoke a method on an object, the actual instances of the types of the object are inspected and compared against all of the available overloaded method signatures. Hence regardless of the declared type of the object, the overloaded method that is selected will be closest to the particular class type and interfaces types of the passed in object.
So how do the languages process the above code? Well Java does the type safe thing and always buys green, unless it know it must be a boy or a girl.
Indirect -------
unknown - Green
baby boy - Green
baby girl - Green
boy - Green
girl - Green
Direct ---------
unknown - Green
baby boy - Green
baby girl - Green
boy - Blue
girl - Pink
Groovy on the other hand, peeks into the babies diaper and buys the appropriate color, unless it can't do that.
Indirect -------
unknown - Green
baby boy - Blue
baby girl - Pink
boy - Blue
girl - Pink
Direct ---------
unknown - Green
baby boy - Blue
baby girl - Pink
boy - Blue
girl - Pink
Comments (2)
How is the Java example different from compile-time type erasure on parameterized types? See: http://angelikalanger.com/GenericsFAQ/FAQSections/ParameterizedTypes.html
At least, I think what you are talking about is compile-time type erasure.
Posted by John "Z-Bo" Zabroski | May 7, 2007 11:33 AM
Posted on May 7, 2007 11:33
Quite a bit different actually. Type erasure is more of a case of static typing where the types are known at compile time and then erased to a more generic type (if I understand erasure properly). Meta-Methods do all of the type matching at the actual time the method is called, that's what is causing the 1000x performance hit on a method call. Erasure abstracts most of it away at compile time.
While in this case it is trivial to insure what the actual type of the parameter to the color() method will be, in general the multiple dispatch technique examines the type at runtime. If you consider that after this file, via various tricks with Groovy we could introduce a class Grrl extends Girl and and method color(Grrl grr) {return "Red";}, do this all at runtime, and the Meta-Object-Protocol will make it work.
Posted by Danno Ferrin
|
May 7, 2007 12:27 PM
Posted on May 7, 2007 12:27