Какой элегантный способ иметь повторно используемый код метакласса в 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 месяцев спустя это новое "сейчас":-)