Wednesday, 3 January 2007

Closures - use case - instanceof

Another 'use case' for the closures proposal following on from yesterday. This time a problematical one - instanceof.

ifInstanceOf

  Object obj = ...;
  ifInstanceOf (String str : obj) {
    // do something with the string
  }

The intention of this closure method is to take the input object 'obj', check if it matches the required type, and then call the closure itself with the typecast object 'str'. Thus, this operation is the closure equivalent of this current operation:

  Object obj = ...;
  if (obj instanceof String) {
    String str = (String) obj;
    // do something with the string
  }

So, how would the implementation of the method look? Well the implementation needs to be generic, as we can't just handle String objects. So, lets use T to represent the type that we are testing:

public static [T] void ifInstanceOf(Object obj, {T=>void} block) {
  if (obj instanceof T) {
    block.invoke((T) obj);
  }
}

Unfortunately, this won't compile, as you can't write obj instanceof T where T is a generic type. The problem is of course erasure. This method has no way of determining what the actual type of T is - String in this example. So what can be done?

One solution would be to pass the type in as an additional parameter from the calling code:

  Object obj = ...;
  ifInstanceOf (String str : obj, String.class) {
    // do something with the string
  }

But that is just rubbish, as you could define a different type as the Class object and get a ClassCastException - ifInstanceOf (String str : obj, Integer.class)!.

A better solution would be for closure blocks to store the actual invoked generic types as instance variables. Then we could write:

public static [T] void ifInstanceOf(Object obj, {T=>void} block) {
  if (block.actualType(0).isInstance(obj)) {
    block.invoke((T) obj);
  }
}

This would be fairly simple to do, and in fact could probably be optimised by the compiler so that the actual types are only stored if the actualType method is called.

I'm not sure if this is the only use-case where the actual types of the closure would be useful, but I suspect it isn't. If you've got another use-case where the actual type would be useful, or any other feedback, please add a comment!

3 comments:

  1. "But that is just rubbish, as you could define a different type as the Class object and get a ClassCastException - ifInstanceOf (String str : obj, Integer.class)"

    String is not Integer, therefore (String str: whatever) { } is not a {Integer => void}, therefore it won't compile.

    Apparently I failed to add 0 and 55 correctly. JRoller's captcha tech sucks.

    ReplyDelete
  2. I think any hacks to magically add limited reification should be held off and if reification is deemed to be needed (which this example doesn't prove), then the process would be clean, and wouldn't involve reverting such hacks.

    Plus, haven't we left casting back in 1.4? I'm doing my best to believe that!

    ReplyDelete
  3. Stephen Colebourne5 January 2007 at 14:03

    Ricky, I agree with your analysis on String/Integer not compiling due to generic checks, so thats at least positive.

    Really, I'm just testing the water here to see if anyone can find more reasons to need the reified type.

    Oh, and you can only leave casting behind if you follow 'pure-OO' methodologies. Many of use don't ;-)

    ReplyDelete