Multimethods in Groovy

Let us look at a simple class called "Person".


class Person {
    private String name;
    public Person(String name) {
        this.name = name;
    }
    public Person() {
        this.name = "";
    }
    
    public boolean equals(Object o) {
        System.out.println("Huh! not comparable!");
        return false;
    }

    // This does not override java.lang.Object.equals(Object) method
    // See also: Java(TM) Puzzlers: Traps, Pitfalls, and Corner Cases 
    // Puzzle 58 (It is easy to overload when you intend to override!)
    public boolean equals(Person emp) {        
        return name.equals(emp.name);
    }

    public static void main(String[] args) {
        Object e1 = new Person("Sundar");
        Object e2 = new Person("Rajan");
        System.out.println(e1.equals(e2));
        System.out.println(e1.equals(e1));
        System.out.println(e1.equals("Sundar"));   
    }
}

Output of the above program is as below:
Huh! not comparable!
false
Huh! not comparable!
false
Huh! not comparable!
false
This is because equals(Object) is selected for all equals calls in the main method. If we change the main as

    public static void main(String[] args) {
        Person e1 = new Person("Sundar");
        Person e2 = new Person("Rajan");
        System.out.println(e1.equals(e2));
        System.out.println(e1.equals(e1));
        System.out.println(e1.equals("Sundar"));   
    }

We get this output:
false
true
Huh! not comparable!
false

This is because javac's overload resolution mechanism has selected the "right" method (at the compile time) for us!

Let us check the output of the unmodified (i.e., main not changed as above) with Groovy. I'll use

Groovy output:

jrunscript -cp ../build/groovy-engine.jar;../lib/groovy-all-1.0-JSR-06.jar  -l groovy Person.java

false
true
Huh! not comparable!
false

Groovy invokes the "correct" method in all cases. This is because Groovy supports multimethods. The method actully invoked depends not only on the "receiver" - but, also on the dynamic type of the arguments. For each equals call, the correct equals method is chosen (regardless of compiled time type declared in the program).

There are two different variants of multimethods:
  • Languages like CLOS support symmetric multimethods, where every argument of the multimethod is treated in the same way. The multimethods cannot belong to particular classes.
  • Encapsulated or asymmetric multimethods are an alternative to symmetric multimethods: these methods belong to a class and hence are encapsulated within a single class - the Groovy.

If you want to know more about "binary methods" (single argument methods where argument type is same as that of the "receiver" - like the equals method), then you may want to refer to On Binary Methods

Comments:

I don't quite get it. The behavior in the Java code seems to be a case of the compiler choosing the most specific method based on the type of the arguments. (JLS section 15.12.2.2) But the wiki entry on multiple dispatch seems to indicate that the "receiver" object's compile time type determines the method that is called. Do the 2 resolution techniques amount to the same thing?

Posted by Bharath R on August 09, 2006 at 09:53 AM IST #

Sorry, I got it now. Its a mix of both, in a sense. In the first invocation, the compile time type of e1 & e2 is Object. Hence, Object.equals(Object) is called ( or rather the implementation of it Person). In the second case, the compile time type of e1 and e2 is Person, and the most specific method in Person is invoked in each case based on the type of the sole argument.

Posted by Bharath R on August 09, 2006 at 10:14 AM IST #

Hi Bharat: As you mentioned, javac does the overload resolution based on compiled time type(s). With Object e1 & e2 declarations, javac would resolve the calls to Object.equals(Object) methods -- which at runtime would end-up as Person.equals(Object) [because of overriding of Object.equals(Object) by Person.equals(Object)]. After changing the declarations as Person e1 & e2, javac resolves the calls to Person.equals(Person) at compile time for first 2 calls for the third it resolves to Person.equals(Object). In the case of Groovy (and multimethods), the actual method called depends on dynamic type of the receiver as well as dynamic types of each of the arguments. So, regardless of declaration type of e1& e2, Groovy calls the "correct" methods (In our example, the first argument's dynamic type - Person or Object determines whether Person.equals(Person) or Person.equals(Object) is called). Hope this helps.

Posted by A. Sundararajan on August 09, 2006 at 12:46 PM IST #

Thanks. Yes. Makes sense now. The selection of the most specific method by javac correctly falls into the single-dispatch scheme of things.

Posted by Bharath R on August 10, 2006 at 05:53 AM IST #

Post a Comment:
Comments are closed for this entry.
About

sundararajan

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today
Bookmarks
Links

No bookmarks in folder

Blogroll

No bookmarks in folder

News

No bookmarks in folder