Какой элегантный способ иметь повторно используемый код метакласса в Groovy?

Я хотел бы применить преобразование метапрограммирования к некоторым из моих классов, скажем, путем добавления методов 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()
  }
}

2 ответа

Определите поведение, которое вы хотите добавить в качестве признака

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

Затем добавьте эту черту в класс

class Person implements MagicPrinter {
  String name
}

Теперь используйте это!

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

Вы можете пойти на смешанный подход:

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"

Или с расширением MetaClass:

class Car {
    String model
}

Car.metaClass.mixin AutoPrint

assert new Car(model: "GT").printModel() == "Car GT"

Потому что 7 месяцев спустя это новое "сейчас":-)

Другие вопросы по тегам