Category
or the Expando Meta-class (which I think is a great name). JRuby allows you to do this to Java classes as well. For example, you can add methods to the ArrayList
class thusly: require "java"
include_class "java.util.ArrayList"
list = ArrayList.new
%w(Red Green Blue).each { |color| list.add(color) }
# Add "first" method to proxy of Java ArrayList class.
class ArrayList
def first
size == 0 ? nil : get(0)
end
end
puts "first item is #{list.first}"
Here, I just crack open the
ArrayList
class and add a first
method (which probably should have been there anyway, no?). When you define a class, Ruby checks its classpath to see if another class of the same name has already been loaded. If it has, it adds the new behavior to the class. It it's too frightening to add a method to the entire class, Ruby gives you the option of adding it to an object instance instead. Consider this:
# Add "last" method only to the list object ... a singleton method.
def list.last
size == 0 ? nil : get(size - 1)
end
puts "last item is #{list.last}"
Here, I add the
last
method just to the instance of the list class (i.e., this list object). That way, you don't add it to the entire class.Many Java and C# developers are shuddering in horror right now. The consensus seems to be that this is just too dangerous. And, like all advanced language features, it can be abused. But here is a counter argument. How many Java and C# developers have a
StringUtils
class in their project? Pretty much everyone. Why? Because the language designers for both languages no only won't allow you to add your own methods to String
, they won't even allow you to subclass it to create your own String
class. Thus, you are forced by the language design to switch from object-oriented coding to procedural coding, passing Strings around like you are writing in C again. Open classes allow you to make your code much better, when used responsibly. One common argument against this feature from paranoid developers is that they don't trust "junior" developers with this kind of power. So, tell the junior developers on the project not to do it! And, if you really hate the additions that have been made, you can also reopen a class and remove methods at runtime, using either
remove_method
(which removes it from a class) or undef_method
(which removes it from the entire inheritance tree).You can add new methods to classes in Java if you want to use Aspects, but:
- uses an entirely different syntax from Java
- you give up tool support in your IDE for the new methods (in fact, it probably won't even compile it properly without some plugins and such)
- it's so much trouble that you just suffer through the ugly, non-OOP
StringUtils
class
In JRuby, you can add methods to existing classes:
- using the natural syntax of the language
- it is supported as much as anything else (we don't have IntelliJ for Ruby...yet), but nothing chokes either
- it is so easy that it is natural
I think this level of power makes developers used to non-dynamic languages queasy. But in the right hands, it can make you code much more expressive, and not pollute your namespaces with lots of made up class names with
Util
and Helper
tagged on the end. Like all advanced features, when used correctly, it makes your code much better. Are open classes evil? No more evil than any advanced language feature.