nosewheelie

Technology, mountain biking, politics & music.

Pimp my (Java) methods

with 7 comments

A friend of mine has been playing around with Scala and asked about its meta-programming ability. In particular, he’s a Ruby fiend, so is used to its nice reflection abilities. Specifically he wanted to be able to explore an API interactively from the console, like the following for an integer in Ruby:

>> 1.class
=> Fixnum
>> 1.methods
=> ["%", "inspect", "<<", "singleton_method_added", "&", "clone", ">>", "method", "round", "public_methods", "instance_variable_defined?", "divmod", "equal?", "freeze", "integer?", "chr", "*", "+", "to_i", "methods", "respond_to?", "-", "upto", "between?", "prec", "truncate", "/", "dup", "instance_variables", "__id__", "modulo", "object_id", "succ", "|", "eql?", "zero?", "require", "~", "id", "to_f", "singleton_methods", "send", "prec_i", "taint", "step", "to_int", "frozen?", "instance_variable_get", "__send__", "^", "instance_of?", "remainder", "to_a", "+@", "nonzero?", "-@", "type", "**", "floor", "<", "protected_methods", "<=>", "instance_eval", "==", "prec_f", "quo", ">", "display", "===", "downto", "id2name", "size", "instance_variable_set", "kind_of?", "abs", "extend", ">=", "next", "to_s", "<=", "coerce", "hash", "ceil", "class", "tainted?", "=~", "private_methods", "gem", "div", "nil?", "untaint", "times", "to_sym", "[]", "is_a?"]
>> Fixnum.methods
=> ["inspect", "private_class_method", "const_missing", "clone", "method", "public_methods", "public_instance_methods", "instance_variable_defined?", "method_defined?", "superclass", "equal?", "freeze", "included_modules", "const_get", "methods", "respond_to?", "module_eval", "class_variables", "dup", "protected_instance_methods", "instance_variables", "public_method_defined?", "__id__", "object_id", "eql?", "const_set", "require", "id", "singleton_methods", "send", "class_eval", "taint", "frozen?", "instance_variable_get", "include?", "private_instance_methods", "__send__", "instance_of?", "private_method_defined?", "to_a", "name", "autoload", "type", "<", "protected_methods", "instance_eval", "<=>", "==", ">", "display", "===", "instance_method", "instance_variable_set", "kind_of?", "extend", "protected_method_defined?", "const_defined?", ">=", "ancestors", "to_s", "<=", "public_class_method", "allocate", "hash", "class", "instance_methods", "tainted?", "=~", "private_methods", "gem", "class_variable_defined?", "induced_from", "nil?", "untaint", "constants", "autoload?", "is_a?"]

Unfortunately Scala’s meta-programming abilities are currently limited to Java’s, which is to say not very good. As I’m in Rails-land at the moment, I asked Tony to have a crack at it, to which, he’s obliged:

class Classs[T](c: Class[T]) {
  lazy val methods = scala.collection.immutable.HashSet(c.getDeclaredMethods: _*) ++ c.getMethods
}

object Classs {
  implicit def c[T](c: Class[T]) = new Classs(c)
}

Here’s how to use it, we ask for all of Integer’s methods that start with a “t”:

scala> classOf[Integer].methods.filter(_.getName.startsWith("t")).foreach(println(_))
public static java.lang.String java.lang.Integer.toBinaryString(int)
public static java.lang.String java.lang.Integer.toHexString(int)
public static java.lang.String java.lang.Integer.toOctalString(int)
public java.lang.String java.lang.Integer.toString()
public static java.lang.String java.lang.Integer.toString(int,int)
public static java.lang.String java.lang.Integer.toString(int)
private static java.lang.String java.lang.Integer.toUnsignedString(int,int)

Tony’s original source: http://fprogramming.org/paste/viewpaste.php?id=107.

Written by Tom Adams

April 21st, 2008 at 12:40 pm

7 Responses to 'Pimp my (Java) methods'

Subscribe to comments with RSS or TrackBack to 'Pimp my (Java) methods'.

  1. The problem is that you’ve now lost the method ordering, which is often useful. Instead of HashSet, you should probably use a List of some kind.

    Daniel Spiewak

    22 Apr 08 at 3:32 am

  2. The ordering is not guaranteed by the reflection APIs, to quote from Class.getDeclaredMethods:

    Returns an array of Method objects reflecting all the methods declared by the class or interface represented by this Class object. This includes public, protected, default (package) access, and private methods, but excludes inherited methods. The elements in the array returned are not sorted and are not in any particular order. This method returns an array of length 0 if the class or interface declares no methods, or if this Class object represents a primitive type, an array class, or void. The class initialization method is not included in the returned array. If the class declares multiple public member methods with the same parameter types, they are all included in the returned array.

    But yeah, I agree that ordering would be nice if we could get it.

    On a side note, I really don’t want to be writing this stuff, it should be part of the standard APIs.

    Tom Adams

    22 Apr 08 at 9:02 am

  3. Does scala support dynamically adding methods to a class ? (static / instance) methods the way Groovy does.
    I want to be able to add methods to a class without extending it - the way prototype library adds methods to built-in javascript implicit objects. Ofcourse groovy and ruby allows that too?

    karthik

    23 Apr 08 at 2:49 am

  4. Not as such, however Scala has something called implicit defs (used in the post above) that achieve the same effect, but in a safe manner, i.e. you can more easily reason about the effects an implicit will have, vs. not knowing what does what in Ruby/Groovy.

    So when you call a function on a type that doesn’t have that particular method defined, the compiler looks to see if there are any implicits in scope that can convert from the type in question, to a type that has the method defined.

    This is why classOf[Integer].methods works, there is no methods function on the Class[T] type, however there is one on the Classs[T] type, and an implicit exists to get between the two types.

    Tom Adams

    23 Apr 08 at 8:20 am

  5. Still, the Ruby version is less verbose to type, and imho is more elegant. This is not to say that we cannot have a library in Scala/Java that is just as elegant to use.

    Abdul Habra

    23 Apr 08 at 11:56 pm

  6. The Ruby version is less typing, because it is less elegant. That is, it’s not the same thing; it’s a weaker, less useful and less practical representation as per the universal type system.

    You can’t have something for nothing.

    Tony Morris

    5 May 08 at 4:58 pm

  7. You can add a “methods” method to Any to achieve pretty much the same thing in Scala. You can format it however you like, but I like seeing each method on a new line as it helps me read the arguments:

    http://minformix.org/blog/index.php?/archives/91-Scala-Querying-an-objects-fields-and-methods-with-reflection.html

    Min Huang

    11 Jun 08 at 6:32 pm

Leave a Reply