6

This may be a duplicate but "as" is an INCREDABLY hard keyword to google, even S.O. ignores "as" as part of query.

So I'm wondering how to implement a class that supports "as" reflexively. For an example class:

class X {
    private val
    public X(def v) {
        val=v
    }
    public asType(Class c) {
        if (c == Integer.class)
            return val as Integer
        if(c == String.class)
            return val as String
    }
}

This allows something like:

new X(3) as String

to work, but doesn't help with:

3 as X

I probably have to attach/modify the "asType" on String and Integer somehow, but I feel any changes like this should be confined to the "X" class... Can the X class either implement a method like:

X fromObject(object)

or somehow modify the String/Integer class from within X. This seems tough since it won't execute any code in X until X is actually used... what if my first usage of X is "3 as X", will X get a chance to override Integer's asType before Groovy tries to call is?

Bill K
  • 62,186
  • 18
  • 105
  • 157

2 Answers2

6

As you say, it's not going to be easy to change the asType method for Integer to accept X as a new type of transformation (especially without destroying the existing functionality).

The best I can think of is to do:

Integer.metaClass.toX = { -> new X( delegate ) }

And then you can call:

3.toX()

I can't think how 3 as X could be done -- as you say, the other way; new X('3') as Integer is relatively easy.


Actually, you can do this:

// Get a handle on the old `asType` method for Integer
def oldAsType = Integer.metaClass.getMetaMethod( "asType", [Class] as Class[] )

// Then write our own
Integer.metaClass.asType = { Class c ->
  if( c == X ) {
    new X( delegate )
  }
  else {
    // if it's not an X, call the original
    oldAsType.invoke( delegate, c )
  }
}

3 as X
tim_yates
  • 167,322
  • 27
  • 342
  • 338
  • The X class could be modified to have some static `fromObject(object)` method and do as tim shows above and replace the `c == X` check and subsequent block with something like `if (c.metaClass.respondsTo(c, "fromObject")) return c.fromObject(delegate);`. So, you'd be making minimal changes to Integer and putting the conversion logic back in your class. And it would flex for new types you want to create. – Brian Henry Nov 16 '12 at 20:51
  • That's really interesting. Could this code be executed without a specific "Setup" procdeure? I guess I'm wondering that if my first reference to X in the code is "4 as X", is there any method I could provide that the classloader would execute automatically before resolving "4 as X" so that the above code would be ready to go, or do you have to have something like "static X.registerAsType()"? Note that "X as 4" would work immediately without any setup, it's just annoying that "4 as X" would not. – Bill K Nov 16 '12 at 22:53
  • Kinda looks like no. Putting the alteration of Integer.asType in a static initializer of X is no good - it seems that X is not initialized in preparation for the `3 as X` execution. I'm not sure if Groovy provides any global startup hooks or not. But I'm curious to know if it does... – Brian Henry Nov 16 '12 at 23:15
  • I don't think it could because it would have to scan every class in the classpath in order to execute them and Java's never done that. I think that with the way groovy works a method like ".classloaded()" on Object wouldn't hurt... This is really close though, thanks for your help! – Bill K Nov 17 '12 at 00:05
2

This keeps the functionality out of the Integer type, and minimizes scope of the effect (which is good or bad depending on what you're looking for).

This category will apply asType from the Integer side.

class IntegerCategory {
    static Object asType(Integer inty, Class c) {
        if(c == X) return new X(inty)
        else return inty.asType(c)
    }
}

use (IntegerCategory) { 
    (3 as X) instanceof X
}
Brian Henry
  • 3,161
  • 1
  • 16
  • 17