В этой заметке я расскажу о некоторых возможностях Groovy, которые обеспечивают его динамичность. В частности:
- Метаклассы
- Перехват вызова методов
- Категории
Получение списков свойств и методов класса
У каждого объекта есть методы properties и methods, которые возвращают отображения для свойств и методов объекта соответственно.
В частности, следующий код распечатывает все свойства и методы объекта:
obj.properties.each{ println it } // Распечатываем свойства
obj.methods.each{ println it } // А теперь методы
Доступ к свойствам и методам через GString
Рассмотрим задачу доступа к полю или методу, имя которого становится известно только в момент выполнения. В Java для этого приходится использовать достаточно недобный механизм Reflection. В Groovy для этой задачи может быть использован GString. В частности, справа от оператора “.” может находится экземпляр строки, значение которой будет использоваться для доступа к свойству или методу объекта. Например:
class Person { // Объявляем новый класс
String name; // Объявляем свойство в классе
}
def p = new Person()
def fn = 'name' // Имя свойства
p."$fn" = 'alex' // Записываем в свойство новое значение
Метаклассы
Динамичность языка Groovy обеспечивается особым механизмом вызова методов: при вызове метода проверяется не только его наличие в классе, но и в специальном объекте, называемом метаклассом. При этом, при первом доступе к свойству metaClass объекта, его метакласс замещается специальным, представляющим из себя Expando, о котором я писал ранее.
К метаклассу объекта можно получить доступ через поле metaClass объекта.
Используя эту особенность, можно добавлять методоы к уже определенным ранее классам. Для этого всего-навсего необходимо определить соответствующий метод в ExpandoMetaClass класса.
Например, можно определить новый метод для всех объектов класса String:
String.metaClass.sizeSqr = {
delegate.size * delegate.size
}
Перехват вызова методов
Особенностью многих динамических языков является возможность вызова “несуществующих” методов, которые перехватываются специальным обработчиком. Groovy также не является исключением:
Любой класс может реализовать интерфейс GroovyInterceptable и определить метод invokeMethod, который будет вызываться при любом вызове метода. Например:
class Service {
def invokeMethod( String method, args ) {
println "Called $method"
}
}
s = new Service()
s.doSomething() // Будет напечатано "Called doSomething"
Второй вариант перехвата методов - добавление метода invokeMethod в метакласс объекта.
Категории
Модификации метаклассов активны на протяжении всего выполнения программы, однако иногда нужен более контролируемый способ добавления методов к классам. Для этого, в Groovy имеется понятие категорий. Категории позволяют ограничить модификацию классов блоком кода.
Категория в Groovy - это класс, содержащий статические методы, расширяющие другие классы. Например, рассмотрим класс:
class PersistenceCategory {
static void save( Object o ) {
// Реализация сохранения объекта
}
static void load( Object o ) {
// Реализация загрузки объекта
}
}
Если его использовать в качестве категории, все объекты в блоке получат методы save и load. Для использования категорий Groovy содержит ключевое слово use. Например:
use( PersistenceCategory ) {
someObject.load(); // Вызывается PersistenceCategory.save( someObject );
...
someObject.save(); // PersistenceCategory.load( someObject );
}