Friday, May 25, 2007

Pattern matching on Java Objects with Scala Extractors

Scala extractors provide a nice way to use pattern matching on common Java objects. In this post I'm going to show a simple use of extractors with Java reflection objects.

More detailed information about Scala extractors can be found in the main website, in the Scala Language Specification section 8.1.7 and in the Matching Object With Patterns paper.

An extractor object can be used to extract simple values from Java objects. For example, the following code defines a extractor object that returns the name of a java.lang.Class.


object ClassName {
def unapply(c : java.lang.Class) =
Some(c.getName())
}


This extractor can be used as follows:



val c = "hola".getClass()

c match {
case ClassName(name) =>
System.out.println("The class name is: "+name)
}



Also extractors can be combined with other Scala pattern matching elements. For example, given the following extractor definitions:


object ClassName {
def unapply(c : java.lang.Class) =
Some(c.getName())
}

object ClassMethods {
def unapply(c : java.lang.Class) =
Some(c.getMethods())
}


object MethodParts {
def unapply(c : java.lang.reflect.Method) =
Some((c.getName(),c.getReturnType(),c.getParameterTypes()))
}


We can write:


def methodsThatReturnString(c : java.lang.Class) =
c match {
case ClassMethods(mts) =>
List.fromArray(
for {val m <- mts
m match {
case MethodParts(_,ClassName("java.lang.String"),_) => true
case _ => false
}} yield m)
}


Another example of this is the following:

Given these definitions:


object MemberClasses {
def unapply(c : java.lang.Class) =
Some(c.getClasses())
}


We can write:


Class.forName("java.util.Map") match {
case c@MemberClasses(Array(ClassName(inner))) =>
System.out.println("The class or interface "+
c.getName()+
" has one inner class/interface called "+
inner)
case _ => System.out.println("No inner classes")
}



With these simple/artificial examples we only covered the surface of what can be done with pattern matching in existing Java libraries. As with F# active patterns in .NET, Scala Extractors seems to be a useful tool to deal with complex object models.