Tuesday, February 14, 2012

Polymorphism and Overloading in Processing/Java

I was trying to do something that I thought was really straightforward, textbook example, really. It was this: I wanted to create an array of objects of type (class) Shape. Something like:


Shape s[] = new Shape[0];


Class Shape should have methods such as draw(), size(), etc. The idea is that I would derive classes such as Square, Circle, etc from shape and be able to call these methods polymorphically. For instance:

for (int i=0; i < s.length; i++) {
   s[i].draw();
}


This is what the Shape class in Processing looked like:


class Shape {
  float x, y; // Center
  float r; // radius
  color c; // fill color
  
  // Constructor 
  Shape (float x, float y, float r, color c) {
    this.x = x; this.y = y; this.r = r; this.c = c;
  } 
  
  // Draws the shape
  void draw () {
  }
}

Classes Circle and Square followed the same pattern, and the draw() method worked as expected. So far, so good. Problems started when I tried to add a method for shape intersection. It would have a signature such as:



  // Returns true if and only if this shape intersects other
  boolean intersects (Shape other) {
    ...
  }


Notice that the method implements an operation between two shapes. It stands to reason that it would be possible to write methods in all derived classes implementing the particular intersection algorithm. For instance, Circle would have a method called 



  boolean intersects (Square other) ...


Thus, if s[i] is a circle and s[j] is a square, then the proper method would be automatically be called for s[i].intersects(s[j]), right? Well, it doesn't work that way. Class inheritance cannot be overloaded! 


I then tried writing all variants explicitly in the base class, i.e.:


class Shape {
   ...

   boolean intersects (Square other) ...
   boolean intersects (Circle other) ...
   ...
}


Sorry. That doesn't work either. Late binding, which is the ability to select the proper method at run time does not work for selecting the proper method based on the argument signature. So, what's the solution? Answer: you have to test for the argument type yourself. Thus, method intersects for class Circle looks like:

  // Returns true if and only if this shape intersects other
  boolean intersects (Shape other) {
    if (other instanceof Square) {
       // Code for circle/square intersection
    } else if (other instanceof Circle) {
       // Code for circle/circle intersection
    }
    ...
  }


If  I was writing a similar code in Python or Ruby, this is what I would have done from the start. This just goes to show how awkward and complicated language mechanisms based on explicitly declared types can get to no avail.





No comments:

Post a Comment