dromodel
dromodel

Reputation: 10203

How do I get something like an anonymous inner class in Groovy?

How do I define an anonymous inner class in Groovy? I saw references that they were supposed to be supported in Groovy 1.7 and I'm using 1.8.

 thread = process.consumeProcessOutput(
   new Appendable() {
     Appendable append(char c) {
       app1.append(c)
       app2.append(c)
       return this
     }

     Appendable append(CharSequence csq) {
       app1.append(csq)
       app2.append(csq)
       return this
     }

     Appendable append(CharSequence csq, int start, int end) {
       app1.append(csq, start, end)
       app2.append(csq, start, end)
       return this
     }
   })

I get an exception with this code:

Caught: groovy.lang.MissingMethodException: No signature of method: java.lang.UNIXProcess.consumeProcessOutput() is applicable for argument types: (MyClass$1) values: [MyClass$1@19c8ef56]

Upvotes: 8

Views: 11607

Answers (4)

Justin Piper
Justin Piper

Reputation: 3274

Your anonymous class should be fine. Since all your methods just delegate to two other instances of Appendable you could also implement it like this:

final tee
tee = {
    final... args
->
    app1.append(*args)
    app2.append(*args)

    return tee
} as Appendable

The * operator causes Groovy to call #append with the contents of args as its arguments.

The MissingMethodException you're getting is because #consumeProcessOutput takes two arguments—one for STDOUT and one for STDERR. It also only reads enough from the output to prevent the process from blocking, so it's probably not what you want here. Try #waitForProcessOutput instead.

final app1 = new StringBuilder()
final app2 = new StringBuilder()

final tee
tee = {
    final... args
->
    app1.append(*args)
    app2.append(*args)

    return tee
} as Appendable

final cmd = 'ps a'
final p   = cmd.execute()

p.waitForProcessOutput tee, tee

println '*' * 80
println app1
println '*' * 80
println app2

Upvotes: 0

tim_yates
tim_yates

Reputation: 171084

As an addition to @ataylor's solution above, it is possible to use the Map as Appendable notation, but it's a bit of a fudge:

Given the test function:

def testAppendable(Appendable appendable) {
    println "appendable = $appendable"
    appendable.append('a' as char).
               append("GROOVY",1,2).
               append("TIM")
}

We can construct our Appendable thusly:

def app
app = [ append:{ a, b=null, c=null ->
          if( a.grep( CharSequence ) ) a = a[ (b?:0)..<(c?:a.length()) ]
          println "Got $a"
          app
        } ] as Appendable

Then, executing

testAppendable( app )

Prints out

appendable = {append=ConsoleScript25$_run_closure1@173a30bd}
Got a
Got R
Got TIM

as expected...

Depending on the situation, I would tend to avoid doing it this way though, as the anonymous class route is far more readable ;-)

Upvotes: 4

ataylor
ataylor

Reputation: 66059

This is a tricky case since the methods have to return the object itself as an Appendable, and has an overloaded method name that doesn't work well with groovy map to interface casting. The simplest, clearest way is probably to just use an anonymous inner class, as you would in Java. This requires a reasonably current version of groovy (1.7 or newer I think):

def testAppendable(Appendable appendable) {
    println "appendable = $appendable"
    appendable.append('a' as char).
               append('b' as char).
               append('c' as char)
}

testAppendable(new Appendable() {
    Appendable append(char c) {
        println "got $c"
        this
    }
    Appendable append(CharSequence csq) {
        this
    }
    Appendable append(CharSequence csq, int start, int end) {
        this
    }
    String toString() { "inner class appendable" }
});

Another alternative would be to use an Expando with closures. It's a bit awkward since only one implementation per method name can be initialized in the constructor. Note that any interface methods omitted are given a default implementation that throws an exception.

testAppendable(new Expando(
    append: { char c ->
        println "got $c"
        delegate as Appendable
    },
    toString: { ->
        "expando appendable"
    }
) as Appendable)

EDIT: Regarding your example, I don't see why it would fail. My test is almost identical and works without any issues. What does the signature of process.consumeProcessOutput look like? Also, you can double check that MyClass$1 implements Appendable by running javap MyClass$1.

Upvotes: 8

Ray Tayek
Ray Tayek

Reputation: 10003

this works in a recent indigo:

List l=new LinkedList() {
            {
                add(new Integer(1));
                add(new Integer(2));
            }
        };
println l

Upvotes: 0

Related Questions