我如何在Groovy中获得类似匿名内部类的东西?

时间:2011-10-25 20:36:31

标签: groovy

如何在Groovy中定义匿名内部类?我看到了他们应该在Groovy 1.7中支持的引用,我使用的是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
     }
   })

我使用此代码获得异常:

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

4 个答案:

答案 0 :(得分:6)

这是一个棘手的情况,因为方法必须将对象本身作为Appendable返回,并且具有重载的方法名称,这对于groovy map to interface casting不起作用。最简单,最清晰的方法可能是使用匿名内部类,就像在Java中一样。这需要一个合理的当前版本的groovy(1.7或更新我认为):

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" }
});

另一种选择是使用带有闭包的Expando。这有点尴尬,因为每个方法名称只能在构造函数中初始化一个实现。请注意,省略的任何接口方法都会被赋予一个抛出异常的默认实现。

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

编辑:关于你的例子,我不明白为什么会失败。我的测试几乎完全相同,没有任何问题。 process.consumeProcessOutput的签名是什么样的?此外,您可以通过运行MyClass$1来仔细检查Appendable实施javap MyClass$1

答案 1 :(得分:3)

作为@ataylor's solution above的补充, 可以使用Map as Appendable表示法,但它有点像软糖:

鉴于测试功能:

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

我们可以这样构建我们的Appendable:

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

然后,执行

testAppendable( app )

打印

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

正如所料......

根据具体情况,我倾向于避免这样做,因为匿名类路线更具可读性; - )

答案 2 :(得分:1)

你的匿名课应该没问题。由于您的所有方法都只委托给Appendable的其他两个实例,您也可以像这样实现它:

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

    return tee
} as Appendable

*运算符使Groovy以#append的内容作为参数调用args

你得到的MissingMethodException是因为#consumeProcessOutput有两个参数 - 一个用于STDOUT,一个用于STDERR。它也只能从输出中读取足够的数据以防止进程被阻塞,所以它可能不是你想要的。请改为#waitForProcessOutput

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

答案 3 :(得分:0)

这适用于最近的靛蓝:

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