« Return to Thread: metaclass with enum

Re: metaclass with enum

by Roshan Dawrani-2 :: Rate this Message:

Reply to Author | View in Thread

Here is another solution, which changes the metaClass of enum instances at runtime without needing to do a global change by calling EMC.enableGlobally() :-

================================================
enum Ville
{
    TOKIO, PARIS
    def metaClass
    MetaClass getMetaClass() {
        if(!metaClass) {
            metaClass = new ExpandoMetaClass(Ville, true, true);
            metaClass.initialize()
        }
        metaClass
    }
    def addProperty(propName, propValue) {
        metaClass = new ExpandoMetaClass(Ville, true, true);
        this.metaClass."$propName" = propValue
        metaClass.initialize();
    }
}

// make the metaclass changes on the enum instances
Ville.values().each { enumInstance ->
    enumInstance.addProperty('parcouru', Integer.MAX_VALUE)
    enumInstance.addProperty('précédent', 0)
}

// check if the enum has now got the new Properties
assert Ville.TOKIO.parcouru == Integer.MAX_VALUE
assert Ville.TOKIO.précédent == 0

// change runtime properties and check on different instances
Ville.TOKIO.parcouru = 10
assert Ville.TOKIO.parcouru == 10
assert Ville.PARIS.parcouru == Integer.MAX_VALUE
println "Done"
=======================================

Hope it helps.
Roshan

On Sat, Nov 7, 2009 at 2:26 PM, Roshan Dawrani <roshandawrani@...> wrote:
Ok, now I get the question fully.

You want to define new properties on the enum but not from inside the enum as I first suggested but at runtime.

I don't know at the moment a good, clean answer to that question - because enums are a little special and their instance are created internally when enum class is loaded, so the changes that you make to enum's metaclass after the enum is fully loaded (and its instances are constructed) are not seen by the enum instances like TOKIO.

Till any better answer comes along, one possible solution is below.

=================================
enum Ville
{
    TOKIO, PARIS
}
// after this already constructed enum instances still pick up enum class level metaclass changes made at runtime
ExpandoMetaClass.enableGlobally()

Ville.metaClass.cool = 10

println Ville.PARIS.cool

// after this enum instance will go back to its own MC again and not see enum class level metaclass changes made at runtime
ExpandoMetaClass.disbleGlobally()

println Ville.PARIS.cool // will fail again
=================================

-- Roshan


On Sat, Nov 7, 2009 at 2:06 PM, Ista Pouss <istaous@...> wrote:
Yes, but how do that on the enum itself ?

As an exemple, I do this code in the groovy web console :

class A {}
enum Ville {TOKIO, PARIS}

assert Ville.TOKIO instanceof Ville // of course.

A.metaClass.cool = 10

a = new A()
assert a.cool == 10

Ville.metaClass.cool = 10
assert Ville.TOKIO.cool == 10 // BING !

I do exactly the same metaClass thing, on a "regular" class (A), and
on an enum (Ville). It's OK for instance class A (a), not for instance
Ville (TOKIO).

The error in groovy web console (http://groovyconsole.appspot.com/) is :

groovy.lang.MissingPropertyException: No such property: cool for class: Ville
       at Script1.run(Script1.groovy:12)



2009/11/7 Roshan Dawrani <roshandawrani@...>:
> In the first mail you wanted to access Ville.PARIS.parcouru, which meant
> that you wanted to have "parcouru" on the enum constant, which was of type
> Ville.
>
> If you want to add property "parcouru" at runtime on some regular class,
> then you can very easily do it. Code below shows 2 ways of doing it:
> =======================================================
> enum Ville {TOKIO, PARIS}
> class AlgoTown
> {
>     Ville ville;
> }
>
> String runtimePropName = "précédent"
> addPropertyWhoseNameIsKnown()
> addPropertyWhoseNameIsNotKnownUntilRuntime(runtimePropName)
>
> def addPropertyWhoseNameIsKnown() {
>     AlgoTown.metaClass.parcouru = Integer.MAX_VALUE
> }
> def addPropertyWhoseNameIsNotKnownUntilRuntime(propName) {
>     AlgoTown.metaClass."$propName" = 0
> }
>
> def algo1 = new AlgoTown()
> def algo2 = new AlgoTown()
>
> println algo1.parcouru // Integer.MAX_VALUE
> println algo1."$runtimePropName" // 0
>
> algo1.parcouru = 10 // change algo1's parcouru property to 10
>
> println algo1.parcouru // 10
> println algo2.parcouru // algo2 still has parcouru = Integer.MAX_VALUE
> =======================================================
>
> Hope it helps.
> Roshan
>
>
> On Sat, Nov 7, 2009 at 12:49 PM, Ista Pouss <istaous@...> wrote:
>>
>> 2009/11/7 Roshan Dawrani <roshandawrani@...>:
>> > Hi,
>> > If you don't have to add enum properties parcouru and précédent
>> > dynamically
>> > at run-time, then you can do it like:
>> >
>> > ==========================================================
>> > enum Ville
>> > {
>> >     TOKIO, MAKAO, OSLO, PARIS, NANTES, ZARASTAPARTAKIS
>> >     Ville() {
>> >         parcouru = Integer.MAX_VALUE
>> >         précédent = 0
>> >     }
>> >     Integer parcouru
>> >     Integer précédent
>> > }
>>
>> Yes, but these properties are not "ville" (ie: town)'s properties.
>> It's properties for the algorithm.
>>
>> In java, I make something like that in this case :
>>
>> class AlgoTown // stupid name, sorry
>> {
>>  Ville ville; // it's the town
>>  int parcouru;
>>  int précédent;
>> }
>>
>> With groovy (i'm a neewbee), my impression is : it is possible to add
>> properties at runtime with metaclass (and expando ? ) thing. Is it a
>> rigth / good impression ? Is it The Good Thing To Do in my case ? Is
>> it possible with "enum" ? How ?
>>
>> ---------------------------------------------------------------------
>> To unsubscribe from this list, please visit:
>>
>>    http://xircles.codehaus.org/manage_email
>>
>>
>
>

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

   http://xircles.codehaus.org/manage_email




 « Return to Thread: metaclass with enum