Home  |  About  |  RSS antonfagerberg.com

Extending instances on creation for debugging

06 Mar 2018

A neat trick you can do in Scala is extending an object (instance of an existing class) on the fly when you instantiate it. This comes in very handy when debugging code you do not own yourself.

As an example, I was working with Javas DigestInputStream to calculate a MD5 checksum on the fly from an InputStream. I wanted to know how the MD5 checksum changed during each partial read of the stream, that is when the read method of DigestInputStream is invoked, and invoke my own method with a local variable.

Here’s what the code looked like:

def myMethod(/* parameters */) = { /* Some implementation */ } 
val localVariable = someThing;
val messageDigest = MessageDigest.getInstance("MD5")
val digestInputStream = new DigestInputStream(inputStream, messageDigest)

The implementation for DigestInputStream is not in my code base, meaning I can’t put things in the read method. You can place a break point on it and invoke code on the fly from your IDE, but then you get into the problem of localVariable and myMethod being out of scope.

Here’s where extending the instance on creation comes in handy. It means is that you can override the read method on your custom instance, invoke the local method myMethod using the local variable localVariable in it and then pass everything to super.read to keep the existing functionality intact.

def myMethod(/* parameters */) = { /* Some implementation */ } 
val localVariable = someThing;
val messageDigest = MessageDigest.getInstance("MD5")
val digestInputStream = new DigestInputStream(inputStream, messageDigest) {
  override def read(b: Array[Byte], off: Int, len: Int) = {
    myMethod(localVariable) // Yay!
    super.read(b, off, len)
  }
}

Alternatively, you can put a break point in your overridden read method to get everything in scope and debug with your IDE.

Happy debugging!