Uploaded image for project: 'Groovy'
  1. Groovy
  2. GROOVY-11143

Add extension method to source unit (same locality as static import)

    XMLWordPrintableJSON

Details

    • New Feature
    • Status: Open
    • Major
    • Resolution: Unresolved
    • None
    • None
    • groovy-jdk
    • None

    Description

      I have been poking around with the many ways to extend a class/object's functionality within groovy. To add a new method that can be called like any other instance method, there are a few possibilities:

      1. modify the MetaClass

      String.metaClass.ext = { ->
        print delegate
      }
      "works".ext()
      

      2. use a category class

      @Category(String) class Cat {
        void ext() {
          println this
        }
      }
      use (Cat) {
        "works".ext()
      }
      

      3. apply a mixin
      first create class similar to Cat in (2)

      def str = "works"; str.metaClass.mixin(Cat)
      str.ext()
      

      4. apply a trait

      trait Ext {
        void ext() { println this.toLowerCase() } // see GROOVY-11142
      }
      "works".withTraits(Ext).ext()
      

      5. create an extension moodule

      • create class similar to Cat in (2)
      • create META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule file
      • build and deploy separately from current script / project

      What I'm after is something that works as easily in a script or source file as static imports but lets me call like an extension method. And does not modify state outside of the current script (like MetaClass) or introduce a thread barrier (like use). And works with static type checking.

      Scala has an "extension" mechanism that is similar to static import but there is a keyword on the category class that lets compiler know the method is used like any other instance method. Scala used to provide "implicit" methods – I've lost my familiarity with those.

      I've seen "import static extension foo.Bar.baz;" for some language. (citation required)

      And lastly, Kotlin provides a local extension like this:

      fun foo.Bar.baz() { x ->
        // ...
      }
      

      For any new users or just to add something quickly that can be used in a file, I think I'm looking for an enhancement to "import static ..." that will get substituted in the source unit, like StaticImportVisitor does today for static method calls. For example:

      import static foo.Bar.baz
      import static /*something*/ foo.Bar.baz
      baz("") // supported by first import
      "".baz() // supported by second import
      

      I have been considering whether "extension", "default", some annotation or something else should be used for /*something*/ in the script above.

      Similar to Kotlin, this could let someone declare an extension in a single script with no dynamic runtime impact:

      import static /*something*/ ThisScript.*
      static ext(String self) {
        print self
      }
      "works".ext()
      

      Has anyone looked at something like this in Groovy or other languages and has some feedback?

      Attachments

        Issue Links

          Activity

            People

              Unassigned Unassigned
              emilles Eric Milles
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

                Created:
                Updated: