【问题标题】:What's an elegant way to have a reusable metaclass code in Groovy?在 Groovy 中拥有可重用元类代码的优雅方式是什么?
【发布时间】:2015-01-19 17:26:15
【问题描述】:

我想对我的一些类应用元编程转换,比如添加 printXxx 方法,如下所示:

class Person {
  String name
}

def p = new Person()
p.printName() // does something

我有一个粗略的想法,一旦我有了一个元类,如何做到这一点:

Person.metaClass.methodMissing = { name, args ->
  delegate.metaClass."$name" = { println delegate."${getPropName(name)}" }
  delegate."$name"(*args)
}

现在如何将这段代码变成一个可重用的“库”?我想做类似的事情:

@HasMagicPrinterMethod
class Person {
  String name
}

class Person {
  String name

  static {
    addMagicPrinters()
  }
}

【问题讨论】:

    标签: groovy metaclass mop expandometaclass


    【解决方案1】:

    定义要添加为特征的行为

    trait MagicPrinter {                           
      void printProperties() {  
        this.properties.each { key, val ->
          println "$key = $val"
        }
      }          
    }
    

    然后将此特征添加到一个类中

    class Person implements MagicPrinter {
      String name
    }
    

    现在使用它!

    new Person(name: 'bob').printProperties()
    

    【讨论】:

    • 我使用联合编译并且永远无法让特征在这样的设置中工作(生成的存根文件无效)。另外,我不想要一种方法(printProperties),我想为每个属性动态生成一种方法(printName、printAge 等)
    【解决方案2】:

    您可以采用混合方法:

    class AutoPrint {
        static def methodMissing(obj, String method, args) {
            if (method.startsWith("print")) {
                def property = (method - "print").with { 
                    it[0].toLowerCase() + it[1..-1] 
                }
                "${obj.getClass().simpleName} ${obj[property]}"
            }
            else {
                throw new NoSuchMethodException()
            }
        }
    }
    

    您可以将其与静态块混合使用:

    class Person {
        static { Person.metaClass.mixin AutoPrint }
        String name
    }
    
    def p = new Person(name: "john doe")
    
    
    try {
        p.captain()
        assert false, "should've failed"
    } catch (e) {
        assert true
    }
    
    assert p.printName() == "Person john doe"
    

    或者用expandoMetaClass:

    class Car {
        String model
    }
    
    Car.metaClass.mixin AutoPrint
    
    assert new Car(model: "GT").printModel() == "Car GT"
    

    因为 7 个月后是新的“现在”:-)

    【讨论】:

      猜你喜欢
      • 2016-07-12
      • 2012-12-20
      • 2013-05-23
      • 1970-01-01
      • 1970-01-01
      • 2010-10-02
      • 2015-03-22
      • 2021-01-04
      相关资源
      最近更新 更多