You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@groovy.apache.org by OC <oc...@ocs.cz> on 2016/04/01 03:48:05 UTC

static propertyMissing in an interface (was: interface/implementation patten)

Hello there,

playing with possibilities of the i/i pattern, I have found one can install a static property to an interface, and then use the property all right -- see a proof-of-concept below.

Since it might be used e.g. to create instances or to get a factory through public API based on interfaces (which would otherwise not be possible without exposing the implementation class), this is truly interesting.

The question is: can I rely on this rather arcane behaviour, that it will not be broken in future Groovy versions?

Thanks,
OC

===
78 /tmp> <q.groovy
class q {
  static main(av) {
    ExpandoMetaClass.enableGlobally()
    Root.metaClass.static.propertyMissing={ name ->
      String getter="get${name.capitalize()}"
      println "... PMISS($name) in $delegate, installing $getter()"
      def body={-> "<$name in $delegate>" }
      delegate.metaClass.static."$getter"=body
      delegate."$getter"() // I wonder why just "body()" does not work?!?
    }

    println "- ${Foo.staticPropertyOfInterfaceWow}"
    println "- ${Bar.itReallyWorks}"
    println "second time, they go directly without PMISS"
    println "- ${Foo.staticPropertyOfInterfaceWow}"
    println "- ${Bar.itReallyWorks}"
  }
}

interface Root {}
interface Foo extends Root {
}
interface Bar extends Foo {
}
79 /tmp> groovy q 
... PMISS(staticPropertyOfInterfaceWow) in interface Foo, installing getStaticPropertyOfInterfaceWow()
- <staticPropertyOfInterfaceWow in interface Foo>
... PMISS(itReallyWorks) in interface Bar, installing getItReallyWorks()
- <itReallyWorks in interface Bar>
second time, they go directly without PMISS
- <staticPropertyOfInterfaceWow in interface Foo>
- <itReallyWorks in interface Bar>
80 /tmp> 
===

Re: static propertyMissing in an interface

Posted by OC <oc...@ocs.cz>.
Jochen,

On 3. 4. 2016, at 22:29, Jochen Theodorou <bl...@gmx.org> wrote:

> On 03.04.2016 17:33, OC wrote:
> [...]
>> ===
>> 6 /tmp> <q.groovy
>> class q {
>>  static main(av) {
>>    ExpandoMetaClass.enableGlobally()
>>    Root.metaClass.static.propertyMissing={ name ->
>>      String getter="get${name.capitalize()}"
>>      def body={-> "<$name in $delegate>" }
>>      delegate.metaClass.static."$getter"=body
>>      body.call() // same problem with body(); delegate."$getter"() would work though
>>    }
>>    println "- ${Root.weird}"
>>  }
>> }
>> interface Root {}
>> 7 /tmp> groovy q.groovy
>> Caught: groovy.lang.MissingMethodException: No signature of method: java.io.StringWriter.capitalize() is applicable for argument types: () values: []
>> groovy.lang.MissingMethodException: No signature of method: java.io.StringWriter.capitalize() is applicable for argument types: () values: []
>> 	at q$_main_closure1.doCall(q.groovy:5)
>> 	at q.main(q.groovy:10)
>> 8 /tmp>
> 
> so I misunderstood... it was not, that somehow body was not recognized as closure or such, it is that the execution of  code inside fails. capitalize() is a method on CharSequence, StringWriter is no CharSequence, so the call fails. Did you add such a method yourself? if not, you need to convert to String first.

Convert what?

I call the capitalize method only once, on the name argument of the propertyMissing closure. That is/should be always a String, should it not? Or is there a valid case where this argument would be something else?

Besides the problem disappears if the "body()" or "body.call()" call gets replaced by "delegate."$getter"()", which is sort of weird, for delegate."$getter" actually contains the very body closure :-O

It looks like the way the body closure captures the name variable is... weird: it seems when body.call() is performed, the propertyMissing closure gets called again, this time with a StringWriter for "name", in a way which is completely obscure to me:

===
14 /tmp> <q.groovy      
class q {
 static main(av) {
   ExpandoMetaClass.enableGlobally()
   Root.metaClass.static.propertyMissing={ String name ->
     def body={-> "<$name in $delegate>" }
     body.call()
   }
   println "- ${Root.weird}"
 }
}
interface Root {}
15 /tmp> groovy q.groovy
Caught: groovy.lang.MissingMethodException: No signature of method: q$_main_closure1.doCall() is applicable for argument types: (java.io.StringWriter) values: [- <weird in ]
...
16 /tmp> 
===

Thank you very much and all the best,
OC


Re: static propertyMissing in an interface

Posted by Jochen Theodorou <bl...@gmx.org>.
On 03.04.2016 17:33, OC wrote:
[...]
> ===
> 6 /tmp> <q.groovy
> class q {
>   static main(av) {
>     ExpandoMetaClass.enableGlobally()
>     Root.metaClass.static.propertyMissing={ name ->
>       String getter="get${name.capitalize()}"
>       def body={-> "<$name in $delegate>" }
>       delegate.metaClass.static."$getter"=body
>       body.call() // same problem with body(); delegate."$getter"() would work though
>     }
>     println "- ${Root.weird}"
>   }
> }
> interface Root {}
> 7 /tmp> groovy q.groovy
> Caught: groovy.lang.MissingMethodException: No signature of method: java.io.StringWriter.capitalize() is applicable for argument types: () values: []
> groovy.lang.MissingMethodException: No signature of method: java.io.StringWriter.capitalize() is applicable for argument types: () values: []
> 	at q$_main_closure1.doCall(q.groovy:5)
> 	at q.main(q.groovy:10)
> 8 /tmp>

so I misunderstood... it was not, that somehow body was not recognized 
as closure or such, it is that the execution of  code inside fails. 
capitalize() is a method on CharSequence, StringWriter is no 
CharSequence, so the call fails. Did you add such a method yourself? if 
not, you need to convert to String first.

bye Jochen


Re: static propertyMissing in an interface

Posted by OC <oc...@ocs.cz>.
Jochen,

On 3. 4. 2016, at 7:23, Jochen Theodorou <bl...@gmx.org> wrote:

> On 01.04.2016 03:48, OC wrote:
>> playing with possibilities of the i/i pattern, I have found one can install a static property to an interface, and then use the property all right -- see a proof-of-concept below.
>> 
>> Since it might be used e.g. to create instances or to get a factory through public API based on interfaces (which would otherwise not be possible without exposing the implementation class), this is truly interesting.
>> 
>> The question is: can I rely on this rather arcane behaviour, that it will not be broken in future Groovy versions?
> 
> if you want to be really sure it will continue working it is best if you contribute a test case ;) But I see no plans to change that, especially with java8 being able to define static methods and thus static properties.

Thanks a lot!

> [...]
>>       def body={-> "<$name in $delegate>" }
>>       delegate.metaClass.static."$getter"=body
>>       delegate."$getter"() // I wonder why just "body()" does not work?!?
> 
> no idea why not... but you can always try body.call() instead.

Same as with body():

===
6 /tmp> <q.groovy 
class q {
 static main(av) {
   ExpandoMetaClass.enableGlobally()
   Root.metaClass.static.propertyMissing={ name ->
     String getter="get${name.capitalize()}"
     def body={-> "<$name in $delegate>" }
     delegate.metaClass.static."$getter"=body
     body.call() // same problem with body(); delegate."$getter"() would work though
   }
   println "- ${Root.weird}"
 }
}
interface Root {}
7 /tmp> groovy q.groovy
Caught: groovy.lang.MissingMethodException: No signature of method: java.io.StringWriter.capitalize() is applicable for argument types: () values: []
groovy.lang.MissingMethodException: No signature of method: java.io.StringWriter.capitalize() is applicable for argument types: () values: []
	at q$_main_closure1.doCall(q.groovy:5)
	at q.main(q.groovy:10)
8 /tmp> 
===

Thanks again and all the best,
OC


Re: static propertyMissing in an interface

Posted by Jochen Theodorou <bl...@gmx.org>.
On 01.04.2016 03:48, OC wrote:
> Hello there,
>
> playing with possibilities of the i/i pattern, I have found one can install a static property to an interface, and then use the property all right -- see a proof-of-concept below.
>
> Since it might be used e.g. to create instances or to get a factory through public API based on interfaces (which would otherwise not be possible without exposing the implementation class), this is truly interesting.
>
> The question is: can I rely on this rather arcane behaviour, that it will not be broken in future Groovy versions?

if you want to be really sure it will continue working it is best if you 
contribute a test case ;) But I see no plans to change that, especially 
with java8 being able to define static methods and thus static properties.

[...]
>        def body={-> "<$name in $delegate>" }
>        delegate.metaClass.static."$getter"=body
>        delegate."$getter"() // I wonder why just "body()" does not work?!?

no idea why not... but you can always try body.call() instead.

bye Jochen