Open Classes

One nice feature of Ruby that I wish I had with Java is the able to change the standard classes. For example, Ruby’s Array class has a map function, but no reduce function. So we simply add methods to the class:

class Array
  def reduce(n)
    each do |value|
      n = yield(n, value)
    end

    n
  end

  def sum(initial = 0)
    reduce(initial) { |n, value| n + value }
  end

  def product(initial = 1)
    reduce(initial) { |n, value| n * value }
  end
end

[ 1, 2, 3, 4, 5 ].sum   »  15 
[ 1, 2, 3, 4, 5 ].product   »  120

(Example from Programming Ruby, where the method is called ‘inject’ and is defined in a separate Mixin so it can be included in other classes, e.g. Range)

In the above example we add the reduce method to the Array class and two additional methods, sum and product, which make use of reduce. The reduce method takes an initial value for the reduction and a code block to act on each element of the array.

We can’t replicate this elegance in Java because the classes are closed. Extending the class only gets us halfway there because only instances of the sub class have the method, i.e. others methods that return the original class won’t have it. Therefore we tend to end up abandoning OO and sticking the function somewhere else, e.g.:

public interface Functor {
  Object function(Object acc, Object value);
}

public class ArrayUtils {
  public static Object reduce(Object[] array, Object initial, Functor f) {
    Object result = initial;
    for (Object o : array) {
      result = f.function(result, o);
    }

    return result;
  }

  public static Integer sum(Integer[] integers) {
    return reduce(integers, 0, new Functor() { 
      public Object function(Object acc, Object value) { return ((Integer) acc) + ((Integer) value); }
    });
  }

  public static Integer product(Integer[] integers) {
    return reduce(integers, 1, new Functor() { 
      public Object function(Object acc, Object value) { return ((Integer) acc) * ((Integer) value) }
    });
  }
}

ArrayUtils.sum(new Integer[] {1, 2, 3, 4, 5});   »  15 
ArrayUtils.product(new Integer[]{1, 2, 3, 4, 5});   »  120

The Java version does the same thing, but just looks wrong to me. Sure autoboxing cleans it up a bit and generics would do more (at the expensive of having to instantiate ArrayUtils), but you can’t get around defining the methods outside of the class, and having code blocks passed around as anonymous classes.

Spread the word: Technorati related  |  Technorati related  |  del.icio.us bookmark it!  |  submit Open Classes digg.com digg it!  |  reddit reddit!

5 Responses to “Open Classes”

  1. Larry Roth says:

    You bring up an interseting point, and your example is a good one. But I have a question…and I may be way off base here…isn’t it important to be able to finalize a class so that you can’t extend it and perhaps have access to private variables that are protected? I am thinking more about API’s than general utilitiy classes, but wouldn’t that maybe provide a security hole? Or am I totally missing the point…

  2. Anibal Rojas says:

    The extreme dinamic nature of Ruby can be dangerous, Matz talks about “the sharp knife” metaphor. Off Topic: PLease considera registering your blog at RubyCorner.com, a meeting place for people interested in the Ruby Programming Language or any of the related technologies. I would suggest using the Ruby feed to register.

  3. Miles says:

    The dynamic nature of Ruby can be dangerous either because the features are use incorrectly, or maliciously. The above example isn’t particularly dangerous because you’d have to include your extra definitions with the running program. If you were trying to do that it would probably be easier to download the source to Ruby, modify it, then distribute the malicious binary. Java gets around this by signing JAR files.

    Then there’s the problem of people misusing features because they don’t understand them. There is an argument for protecting programmers against themselves, and Java tries to do that, but if the best solution is to use those advanced features you can end up with some convoluted code (see Java example above).

  4. JD says:

    What is wrong with anonymous classes? They provide quite a bit of power. You tend to use anonymous code blocks in perl a lot. They help reduce the coupling of your code.

  5. Miles says:

    There’s nothing wrong with inner classes, they are very useful, especially in GUI programming. What I don’t like is the amount of cruft that goes with them, I just want to pass a function pointer or a code block for the receiver to execute. In Java I need to define the pattern of the function via an interface, then implement that class in my method call, which to me doesn’t feel or look right, it should be a lot simpler.

Leave a Reply

Line and paragraph breaks automatic.
XHTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>